Changeset 6127 in josm for trunk/src


Ignore:
Timestamp:
2013-08-09T18:05:11+02:00 (11 years ago)
Author:
bastiK
Message:

applied #8895 - Upgrade metadata-extractor to v. 2.6.4 (patch by ebourg)

Location:
trunk/src
Files:
24 added
7 deleted
55 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/com/drew/imaging/PhotographicConversions.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.imaging;
     
    1723/**
    1824 * Contains helper methods that perform photographic conversions.
     25 *
     26 * @author Drew Noakes http://drewnoakes.com
    1927 */
    20 public class PhotographicConversions
     28public final class PhotographicConversions
    2129{
    2230    public final static double ROOT_TWO = Math.sqrt(2);
    2331
    24     private PhotographicConversions()
    25     {}
     32    private PhotographicConversions() throws Exception
     33    {
     34        throw new Exception("Not intended for instantiation.");
     35    }
    2636
    2737    /**
    2838     * Converts an aperture value to its corresponding F-stop number.
     39     *
    2940     * @param aperture the aperture value to convert
    3041     * @return the F-stop number of the specified aperture
     
    3243    public static double apertureToFStop(double aperture)
    3344    {
    34         double fStop = Math.pow(ROOT_TWO, aperture);
    35         return fStop;
     45        return Math.pow(ROOT_TWO, aperture);
    3646
    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...
    3948        // fStop = (float)Math.exp(aperture * Math.log(2) * 0.5));
    4049    }
     
    4251    /**
    4352     * Converts a shutter speed to an exposure time.
     53     *
    4454     * @param shutterSpeed the shutter speed to convert
    4555     * @return the exposure time of the specified shutter speed
     
    4757    public static double shutterSpeedToExposureTime(double shutterSpeed)
    4858    {
    49         return (float)(1 / Math.exp(shutterSpeed * Math.log(2)));
     59        return (float) (1 / Math.exp(shutterSpeed * Math.log(2)));
    5060    }
    5161}
  • trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.imaging.jpeg;
    1822
    19 import com.drew.metadata.Directory;
     23import com.drew.lang.ByteArrayReader;
     24import com.drew.lang.annotations.NotNull;
    2025import com.drew.metadata.Metadata;
    21 import com.drew.metadata.MetadataException;
    22 import com.drew.metadata.Tag;
    23 import com.drew.metadata.exif.ExifDirectory;
    2426import com.drew.metadata.exif.ExifReader;
    2527import com.drew.metadata.iptc.IptcReader;
    2628import com.drew.metadata.jpeg.JpegCommentReader;
     29import com.drew.metadata.jpeg.JpegDirectory;
    2730import com.drew.metadata.jpeg.JpegReader;
    2831
     
    3033import java.io.IOException;
    3134import java.io.InputStream;
    32 import java.util.Iterator;
    3335
    3436/**
     37 * Obtains all available metadata from Jpeg formatted files.
    3538 *
     39 * @author Drew Noakes http://drewnoakes.com
    3640 */
    3741public class JpegMetadataReader
    3842{
     43    // TODO investigate supporting javax.imageio
    3944//    public static Metadata readMetadata(IIOMetadata metadata) throws JpegProcessingException {}
    4045//    public static Metadata readMetadata(ImageInputStream in) throws JpegProcessingException{}
     
    4247//    public static Metadata readMetadata(ImageReader reader) throws JpegProcessingException{}
    4348
    44     public static Metadata readMetadata(InputStream in) throws JpegProcessingException
     49    @NotNull
     50    public static Metadata readMetadata(@NotNull InputStream inputStream) throws JpegProcessingException
    4551    {
    46         JpegSegmentReader segmentReader = new JpegSegmentReader(in);
    47         return extractMetadataFromJpegSegmentReader(segmentReader);
     52        return readMetadata(inputStream, true);
    4853    }
    4954
    50     public static Metadata readMetadata(File file) throws JpegProcessingException
     55    @NotNull
     56    public static Metadata readMetadata(@NotNull InputStream inputStream, final boolean waitForBytes) throws JpegProcessingException
     57    {
     58        JpegSegmentReader segmentReader = new JpegSegmentReader(inputStream, waitForBytes);
     59        return extractMetadataFromJpegSegmentReader(segmentReader.getSegmentData());
     60    }
     61
     62    @NotNull
     63    public static Metadata readMetadata(@NotNull File file) throws JpegProcessingException, IOException
    5164    {
    5265        JpegSegmentReader segmentReader = new JpegSegmentReader(file);
    53         return extractMetadataFromJpegSegmentReader(segmentReader);
     66        return extractMetadataFromJpegSegmentReader(segmentReader.getSegmentData());
    5467    }
    5568
    56     public static Metadata extractMetadataFromJpegSegmentReader(JpegSegmentReader segmentReader)
     69    @NotNull
     70    public static Metadata extractMetadataFromJpegSegmentReader(@NotNull JpegSegmentData segmentReader)
    5771    {
    5872        final Metadata metadata = new Metadata();
    59         try {
    60             byte[] exifSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APP1);
    61             new ExifReader(exifSegment).extract(metadata);
    62         } catch (JpegProcessingException e) {
    63             // in the interests of catching as much data as possible, continue
    64             // TODO lodge error message within exif directory?
     73
     74        // Loop through looking for all SOFn segments.  When we find one, we know what type of compression
     75        // was used for the JPEG, and we can process the JPEG metadata in the segment too.
     76        for (byte i = 0; i < 16; i++) {
     77            // There are no SOF4 or SOF12 segments, so don't bother
     78            if (i == 4 || i == 12)
     79                continue;
     80            // Should never have more than one SOFn for a given 'n'.
     81            byte[] jpegSegment = segmentReader.getSegment((byte)(JpegSegmentReader.SEGMENT_SOF0 + i));
     82            if (jpegSegment == null)
     83                continue;
     84            JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class);
     85            directory.setInt(JpegDirectory.TAG_JPEG_COMPRESSION_TYPE, i);
     86            new JpegReader().extract(new ByteArrayReader(jpegSegment), metadata);
     87            break;
    6588        }
    6689
    67         try {
    68             byte[] iptcSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APPD);
    69             new IptcReader(iptcSegment).extract(metadata);
    70         } catch (JpegProcessingException e) {
    71             // TODO lodge error message within iptc directory?
     90        // There should never be more than one COM segment.
     91        byte[] comSegment = segmentReader.getSegment(JpegSegmentReader.SEGMENT_COM);
     92        if (comSegment != null)
     93            new JpegCommentReader().extract(new ByteArrayReader(comSegment), metadata);
     94
     95        // Loop through all APP1 segments, checking the leading bytes to identify the format of each.
     96        for (byte[] app1Segment : segmentReader.getSegments(JpegSegmentReader.SEGMENT_APP1)) {
     97            if (app1Segment.length > 3 && "EXIF".equalsIgnoreCase(new String(app1Segment, 0, 4)))
     98                new ExifReader().extract(new ByteArrayReader(app1Segment), metadata);
     99
     100            //if (app1Segment.length > 27 && "http://ns.adobe.com/xap/1.0/".equalsIgnoreCase(new String(app1Segment, 0, 28)))
     101            //    new XmpReader().extract(new ByteArrayReader(app1Segment), metadata);
    72102        }
    73103
    74                 try {
    75                         byte[] jpegSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_SOF0);
    76                         new JpegReader(jpegSegment).extract(metadata);
    77                 } catch (JpegProcessingException e) {
    78                         // TODO lodge error message within jpeg directory?
    79                 }
    80 
    81                 try {
    82                         byte[] jpegCommentSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_COM);
    83                         new JpegCommentReader(jpegCommentSegment).extract(metadata);
    84                 } catch (JpegProcessingException e) {
    85                         // TODO lodge error message within jpegcomment directory?
    86                 }
     104        // Loop through all APPD segments, checking the leading bytes to identify the format of each.
     105        for (byte[] appdSegment : segmentReader.getSegments(JpegSegmentReader.SEGMENT_APPD)) {
     106            if (appdSegment.length > 12 && "Photoshop 3.0".compareTo(new String(appdSegment, 0, 13))==0) {
     107                //new PhotoshopReader().extract(new ByteArrayReader(appdSegment), metadata);
     108            } else {
     109                // TODO might be able to check for a leading 0x1c02 for IPTC data...
     110                new IptcReader().extract(new ByteArrayReader(appdSegment), metadata);
     111            }
     112        }
    87113
    88114        return metadata;
    89115    }
    90116
    91     private JpegMetadataReader()
     117    private JpegMetadataReader() throws Exception
    92118    {
    93     }
    94 
    95     public static void main(String[] args) throws MetadataException, IOException
    96     {
    97         Metadata metadata = null;
    98         try {
    99             metadata = JpegMetadataReader.readMetadata(new File(args[0]));
    100         } catch (Exception e) {
    101             e.printStackTrace(System.err);
    102             System.exit(1);
    103         }
    104 
    105         // iterate over the exif data and print to System.out
    106         Iterator directories = metadata.getDirectoryIterator();
    107         while (directories.hasNext()) {
    108             Directory directory = (Directory)directories.next();
    109             Iterator tags = directory.getTagIterator();
    110             while (tags.hasNext()) {
    111                 Tag tag = (Tag)tags.next();
    112                 try {
    113                     System.out.println("[" + directory.getName() + "] " + tag.getTagName() + " = " + tag.getDescription());
    114                 } catch (MetadataException e) {
    115                     System.err.println(e.getMessage());
    116                     System.err.println(tag.getDirectoryName() + " " + tag.getTagName() + " (error)");
    117                 }
    118             }
    119             if (directory.hasErrors()) {
    120                 Iterator errors = directory.getErrors();
    121                 while (errors.hasNext()) {
    122                     System.out.println("ERROR: " + errors.next());
    123                 }
    124             }
    125         }
    126 
    127         if (args.length>1 && args[1].trim().equals("/thumb"))
    128         {
    129             ExifDirectory directory = (ExifDirectory)metadata.getDirectory(ExifDirectory.class);
    130             if (directory.containsThumbnail())
    131             {
    132                 System.out.println("Writing thumbnail...");
    133                 directory.writeThumbnail(args[0].trim() + ".thumb.jpg");
    134             }
    135             else
    136             {
    137                 System.out.println("No thumbnail data exists in this image");
    138             }
    139         }
     119        throw new Exception("Not intended for instantiation");
    140120    }
    141121}
     122
  • trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java

    r4231 r6127  
    11/*
    2  * JpegProcessingException.java
     2 * Copyright 2002-2012 Drew Noakes
    33 *
    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
    87 *
    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
    119 *
    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.
    1615 *
    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/
    1820 */
    1921package com.drew.imaging.jpeg;
    2022
    21 import com.drew.lang.CompoundException;
     23import com.drew.imaging.ImageProcessingException;
     24import com.drew.lang.annotations.Nullable;
    2225
    2326/**
    24  * An exception class thrown upon unexpected and fatal conditions while processing
    25  * a Jpeg file.
    26  * @author  Drew Noakes http://drewnoakes.com
     27 * An exception class thrown upon unexpected and fatal conditions while processing a Jpeg file.
     28 *
     29 * @author Drew Noakes http://drewnoakes.com
    2730 */
    28 public class JpegProcessingException extends CompoundException
     31public class JpegProcessingException extends ImageProcessingException
    2932{
    30     public JpegProcessingException(String message)
     33    private static final long serialVersionUID = -7870179776125450158L;
     34
     35    public JpegProcessingException(@Nullable String message)
    3136    {
    3237        super(message);
    3338    }
    3439
    35     public JpegProcessingException(String message, Throwable cause)
     40    public JpegProcessingException(@Nullable String message, @Nullable Throwable cause)
    3641    {
    3742        super(message, cause);
    3843    }
    3944
    40     public JpegProcessingException(Throwable cause)
     45    public JpegProcessingException(@Nullable Throwable cause)
    4146    {
    4247        super(cause);
  • trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1420 */
    1521package com.drew.imaging.jpeg;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1625
    1726import java.io.*;
     
    2231/**
    2332 * 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 about only the non-image
     33 * within the Jpeg.  For example, it may be convenient to store only the non-image
    2534 * 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
    2640 */
    2741public class JpegSegmentData implements Serializable
    2842{
    29     static final long serialVersionUID = 7110175216435025451L;
     43    private static final long serialVersionUID = 7110175216435025451L;
    3044   
    3145    /** A map of byte[], keyed by the segment marker */
    32     private final HashMap _segmentDataMap;
    33 
    34     public JpegSegmentData()
    35     {
    36         _segmentDataMap = new HashMap(10);
    37     }
    38 
    39     public void addSegment(byte segmentMarker, byte[] segmentBytes)
    40     {
    41         List segmentList = getOrCreateSegmentList(segmentMarker);
     46    @NotNull
     47    private final HashMap<Byte, List<byte[]>> _segmentDataMap = new HashMap<Byte, List<byte[]>>(10);
     48
     49    /**
     50     * Adds segment bytes to the collection.
     51     * @param segmentMarker
     52     * @param segmentBytes
     53     */
     54    @SuppressWarnings({ "MismatchedQueryAndUpdateOfCollection" })
     55    public void addSegment(byte segmentMarker, @NotNull byte[] segmentBytes)
     56    {
     57        final List<byte[]> segmentList = getOrCreateSegmentList(segmentMarker);
    4258        segmentList.add(segmentBytes);
    4359    }
    4460
     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
    4567    public byte[] getSegment(byte segmentMarker)
    4668    {
     
    4870    }
    4971
     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
    5080    public byte[] getSegment(byte segmentMarker, int occurrence)
    5181    {
    52         final List segmentList = getSegmentList(segmentMarker);
     82        final List<byte[]> segmentList = getSegmentList(segmentMarker);
    5383
    5484        if (segmentList==null || segmentList.size()<=occurrence)
    5585            return null;
    5686        else
    57             return (byte[]) segmentList.get(occurrence);
    58     }
    59 
     87            return segmentList.get(occurrence);
     88    }
     89
     90    /**
     91     * Returns all instances of a given Jpeg segment.  If no instances exist, an empty sequence is returned.
     92     *
     93     * @param segmentMarker a number which identifies the type of Jpeg segment being queried
     94     * @return zero or more byte arrays, each holding the data of a Jpeg segment
     95     */
     96    @NotNull
     97    public Iterable<byte[]> getSegments(byte segmentMarker)
     98    {
     99        final List<byte[]> segmentList = getSegmentList(segmentMarker);
     100        return segmentList==null ? new ArrayList<byte[]>() : segmentList;
     101    }
     102
     103    @Nullable
     104    public List<byte[]> getSegmentList(byte segmentMarker)
     105    {
     106        return _segmentDataMap.get(Byte.valueOf(segmentMarker));
     107    }
     108
     109    @NotNull
     110    private List<byte[]> getOrCreateSegmentList(byte segmentMarker)
     111    {
     112        List<byte[]> segmentList;
     113        if (_segmentDataMap.containsKey(segmentMarker)) {
     114            segmentList = _segmentDataMap.get(segmentMarker);
     115        } else {
     116            segmentList = new ArrayList<byte[]>();
     117            _segmentDataMap.put(segmentMarker, segmentList);
     118        }
     119        return segmentList;
     120    }
     121
     122    /**
     123     * Returns the count of segment data byte arrays stored for a given segment marker.
     124     * @param segmentMarker identifies the required segment
     125     * @return the segment count (zero if no segments exist).
     126     */
    60127    public int getSegmentCount(byte segmentMarker)
    61128    {
    62         final List segmentList = getSegmentList(segmentMarker);
    63         if (segmentList==null)
    64             return 0;
    65         else
    66             return segmentList.size();
    67     }
    68 
     129        final List<byte[]> segmentList = getSegmentList(segmentMarker);
     130        return segmentList == null ? 0 : segmentList.size();
     131    }
     132
     133    /**
     134     * Removes a specified instance of a segment's data from the collection.  Use this method when more than one
     135     * occurrence of segment data for a given marker exists.
     136     * @param segmentMarker identifies the required segment
     137     * @param occurrence the zero-based index of the segment occurrence to remove.
     138     */
     139    @SuppressWarnings({ "MismatchedQueryAndUpdateOfCollection" })
    69140    public void removeSegmentOccurrence(byte segmentMarker, int occurrence)
    70141    {
    71         final List segmentList = (List)_segmentDataMap.get(new Byte(segmentMarker));
     142        final List<byte[]> segmentList = _segmentDataMap.get(Byte.valueOf(segmentMarker));
    72143        segmentList.remove(occurrence);
    73144    }
    74145
     146    /**
     147     * Removes all segments from the collection having the specified marker.
     148     * @param segmentMarker identifies the required segment
     149     */
    75150    public void removeSegment(byte segmentMarker)
    76151    {
    77         _segmentDataMap.remove(new Byte(segmentMarker));
    78     }
    79 
    80     private List getSegmentList(byte segmentMarker)
    81     {
    82         return (List)_segmentDataMap.get(new Byte(segmentMarker));
    83     }
    84 
    85     private List getOrCreateSegmentList(byte segmentMarker)
    86     {
    87         List segmentList;
    88         Byte key = new Byte(segmentMarker);
    89         if (_segmentDataMap.containsKey(key)) {
    90             segmentList = (List)_segmentDataMap.get(key);
    91         } else {
    92             segmentList = new ArrayList();
    93             _segmentDataMap.put(key, segmentList);
    94         }
    95         return segmentList;
    96     }
    97 
     152        _segmentDataMap.remove(Byte.valueOf(segmentMarker));
     153    }
     154
     155    /**
     156     * Determines whether data is present for a given segment marker.
     157     * @param segmentMarker identifies the required segment
     158     * @return true if data exists, otherwise false
     159     */
    98160    public boolean containsSegment(byte segmentMarker)
    99161    {
    100         return _segmentDataMap.containsKey(new Byte(segmentMarker));
    101     }
    102 
    103     public static void ToFile(File file, JpegSegmentData segmentData) throws IOException
    104     {
    105         ObjectOutputStream outputStream = null;
     162        return _segmentDataMap.containsKey(Byte.valueOf(segmentMarker));
     163    }
     164
     165    /**
     166     * Serialises the contents of a JpegSegmentData to a file.
     167     * @param file to file to write from
     168     * @param segmentData the data to write
     169     * @throws IOException if problems occur while writing
     170     */
     171    public static void toFile(@NotNull File file, @NotNull JpegSegmentData segmentData) throws IOException
     172    {
     173        FileOutputStream fileOutputStream = null;
    106174        try
    107175        {
    108             outputStream = new ObjectOutputStream(new FileOutputStream(file));
    109             outputStream.writeObject(segmentData);
     176            fileOutputStream = new FileOutputStream(file);
     177            new ObjectOutputStream(fileOutputStream).writeObject(segmentData);
    110178        }
    111179        finally
    112180        {
    113             if (outputStream!=null)
    114                 outputStream.close();
    115         }
    116     }
    117 
    118     public static JpegSegmentData FromFile(File file) throws IOException, ClassNotFoundException
     181            if (fileOutputStream!=null)
     182                fileOutputStream.close();
     183        }
     184    }
     185
     186    /**
     187     * Deserialises the contents of a JpegSegmentData from a file.
     188     * @param file the file to read from
     189     * @return the JpegSegmentData as read
     190     * @throws IOException if problems occur while reading
     191     * @throws ClassNotFoundException if problems occur while deserialising
     192     */
     193    @NotNull
     194    public static JpegSegmentData fromFile(@NotNull File file) throws IOException, ClassNotFoundException
    119195    {
    120196        ObjectInputStream inputStream = null;
  • trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java

    r4231 r6127  
    11/*
    2  * JpegSegmentReader.java
    3  *
    4  * This class written by Drew Noakes, in accordance with the Jpeg specification.
    5  *
    6  * This is public domain software - that is, you can do whatever you 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.
    10  *
    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.
    13  *
    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/
    18  *
    19  * Created by dnoakes on 04-Nov-2002 00:54:00 using IntelliJ IDEA
     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/
    2020 */
    2121package com.drew.imaging.jpeg;
    2222
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25
    2326import java.io.*;
    2427
    2528/**
    2629 * Performs read functions of Jpeg files, returning specific file segments.
    27  * TODO add a findAvailableSegments() method
    28  * TODO add more segment identifiers
    29  * TODO add a getSegmentDescription() method, returning for example 'App1 application data segment, commonly containing Exif data'
    3030 * @author  Drew Noakes http://drewnoakes.com
    3131 */
    3232public class JpegSegmentReader
    3333{
    34     // Jpeg data can be sourced from either a file, byte[] or InputStream
    35 
    36     /** Jpeg file */
    37     private final File _file;
    38     /** Jpeg data as byte array */
    39     private final byte[] _data;
    40     /** Jpeg data as an InputStream */
    41     private final InputStream _stream;
    42 
    43     private JpegSegmentData _segmentData;
    44 
    45     /**
    46      * Private, because this segment crashes my algorithm, and searching for
    47      * it doesn't work (yet).
     34    // TODO add a findAvailableSegments() method
     35    // TODO add more segment identifiers
     36    // TODO add a getSegmentDescription() method, returning for example 'App1 application data segment, commonly containing Exif data'
     37
     38    @NotNull
     39    private final JpegSegmentData _segmentData;
     40
     41    /**
     42     * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet).
    4843     */
    4944    private static final byte SEGMENT_SOS = (byte)0xDA;
     
    5449    private static final byte MARKER_EOI = (byte)0xD9;
    5550
    56     /** APP0 Jpeg segment identifier -- Jfif data. */
     51    /** APP0 Jpeg segment identifier -- JFIF data (also JFXX apparently). */
    5752    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. */
    5954    public static final byte SEGMENT_APP1 = (byte)0xE1;
    6055    /** APP2 Jpeg segment identifier. */
     
    7469    /** APP9 Jpeg segment identifier. */
    7570    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. */
    7772    public static final byte SEGMENT_APPA = (byte)0xEA;
    78     /** APPB Jpeg segment identifier. */
     73    /** APPB (App11) Jpeg segment identifier. */
    7974    public static final byte SEGMENT_APPB = (byte)0xEB;
    80     /** APPC Jpeg segment identifier. */
     75    /** APPC (App12) Jpeg segment identifier. */
    8176    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. */
    8378    public static final byte SEGMENT_APPD = (byte)0xED;
    84     /** APPE Jpeg segment identifier. */
     79    /** APPE (App14) Jpeg segment identifier. */
    8580    public static final byte SEGMENT_APPE = (byte)0xEE;
    86     /** APPF Jpeg segment identifier. */
     81    /** APPF (App15) Jpeg segment identifier. */
    8782    public static final byte SEGMENT_APPF = (byte)0xEF;
    8883    /** Start Of Image segment identifier. */
     
    10196     * @param file the Jpeg file to read segments from
    10297     */
    103     public JpegSegmentReader(File file) throws JpegProcessingException
    104     {
    105         _file = file;
    106         _data = null;
    107         _stream = null;
    108 
    109         readSegments();
     98    @SuppressWarnings({ "ConstantConditions" })
     99    public JpegSegmentReader(@NotNull File file) throws JpegProcessingException, IOException
     100    {
     101        if (file==null)
     102            throw new NullPointerException();
     103
     104        InputStream inputStream = null;
     105        try {
     106            inputStream = new FileInputStream(file);
     107            _segmentData = readSegments(new BufferedInputStream(inputStream), false);
     108        } finally {
     109            if (inputStream != null)
     110                inputStream.close();
     111        }
    110112    }
    111113
     
    114116     * @param fileContents the byte array containing Jpeg data
    115117     */
    116     public JpegSegmentReader(byte[] fileContents) throws JpegProcessingException
    117     {
    118         _file = null;
    119         _data = fileContents;
    120         _stream = null;
    121 
    122         readSegments();
    123     }
    124 
    125     public JpegSegmentReader(InputStream in) throws JpegProcessingException
    126     {
    127         _stream = in;
    128         _file = null;
    129         _data = null;
    130        
    131         readSegments();
    132     }
    133 
    134     public JpegSegmentReader(JpegSegmentData segmentData)
    135     {
    136         _file = null;
    137         _data = null;
    138         _stream = null;
    139 
    140         _segmentData = segmentData;
     118    @SuppressWarnings({ "ConstantConditions" })
     119    public JpegSegmentReader(@NotNull byte[] fileContents) throws JpegProcessingException
     120    {
     121        if (fileContents==null)
     122            throw new NullPointerException();
     123
     124        BufferedInputStream stream = new BufferedInputStream(new ByteArrayInputStream(fileContents));
     125        _segmentData = readSegments(stream, false);
     126    }
     127
     128    /**
     129     * Creates a JpegSegmentReader for an InputStream.
     130     * @param inputStream the InputStream containing Jpeg data
     131     */
     132    @SuppressWarnings({ "ConstantConditions" })
     133    public JpegSegmentReader(@NotNull InputStream inputStream, boolean waitForBytes) throws JpegProcessingException
     134    {
     135        if (inputStream==null)
     136            throw new NullPointerException();
     137
     138        BufferedInputStream bufferedInputStream = inputStream instanceof BufferedInputStream
     139                ? (BufferedInputStream)inputStream
     140                : new BufferedInputStream(inputStream);
     141
     142        _segmentData = readSegments(bufferedInputStream, waitForBytes);
    141143    }
    142144
     
    146148     * @param segmentMarker the byte identifier for the desired segment
    147149     * @return the byte array if found, else null
    148      * @throws JpegProcessingException for any problems processing the Jpeg data,
    149      *         including inner IOExceptions
    150      */
    151     public byte[] readSegment(byte segmentMarker) throws JpegProcessingException
     150     */
     151    @Nullable
     152    public byte[] readSegment(byte segmentMarker)
    152153    {
    153154        return readSegment(segmentMarker, 0);
     
    155156
    156157    /**
    157      * Reads the first instance of a given Jpeg segment, returning the contents as
    158      * a byte array.
     158     * Reads the Nth instance of a given Jpeg segment, returning the contents as a byte array.
     159     *
    159160     * @param segmentMarker the byte identifier for the desired segment
    160161     * @param occurrence the occurrence of the specified segment within the jpeg file
    161162     * @return the byte array if found, else null
    162163     */
     164    @Nullable
    163165    public byte[] readSegment(byte segmentMarker, int occurrence)
    164166    {
     
    166168    }
    167169
     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     */
    168187    public final int getSegmentCount(byte segmentMarker)
    169188    {
     
    171190    }
    172191
     192    /**
     193     * Returns the JpegSegmentData object used by this reader.
     194     * @return the JpegSegmentData object.
     195     */
     196    @NotNull
    173197    public final JpegSegmentData getSegmentData()
    174198    {
     
    176200    }
    177201
    178     private void readSegments() throws JpegProcessingException
    179     {
    180         _segmentData = new JpegSegmentData();
    181 
    182         BufferedInputStream inStream = getJpegInputStream();
     202    @NotNull
     203    private JpegSegmentData readSegments(@NotNull final BufferedInputStream jpegInputStream, boolean waitForBytes) throws JpegProcessingException
     204    {
     205        JpegSegmentData segmentData = new JpegSegmentData();
     206
    183207        try {
    184208            int offset = 0;
    185209            // 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)
    187212                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
    189217            offset += 2;
    190218            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
    191223                // next byte is 0xFF
    192                 byte segmentIdentifier = (byte)(inStream.read() & 0xFF);
     224                byte segmentIdentifier = (byte)(jpegInputStream.read() & 0xFF);
    193225                if ((segmentIdentifier & 0xFF) != 0xFF) {
    194226                    throw new JpegProcessingException("expected jpeg segment start identifier 0xFF at offset " + offset + ", not 0x" + Integer.toHexString(segmentIdentifier & 0xFF));
     
    196228                offset++;
    197229                // next byte is <segment-marker>
    198                 byte thisSegmentMarker = (byte)(inStream.read() & 0xFF);
     230                byte thisSegmentMarker = (byte)(jpegInputStream.read() & 0xFF);
    199231                offset++;
    200232                // next 2-bytes are <segment-size>: [high-byte] [low-byte]
    201233                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.");
    203236                offset += 2;
    204237                int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF);
    205238                // segment length includes size bytes, so subtract two
    206239                segmentLength -= 2;
    207                 if (segmentLength > inStream.available())
     240                if (!checkForBytesOnStream(jpegInputStream, segmentLength, waitForBytes))
    208241                    throw new JpegProcessingException("segment size would extend beyond file stream length");
    209                 else if (segmentLength < 0)
     242                if (segmentLength < 0)
    210243                    throw new JpegProcessingException("segment size would be less than zero");
    211244                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.");
    213247                offset += segmentLength;
    214248                if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF)) {
     
    216250                    // have to search for the two bytes: 0xFF 0xD9 (EOI).
    217251                    // It comes last so simply return at this point
    218                     return;
     252                    return segmentData;
    219253                } else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF)) {
    220254                    // the 'End-Of-Image' segment -- this should never be found in this fashion
    221                     return;
     255                    return segmentData;
    222256                } else {
    223                     _segmentData.addSegment(thisSegmentMarker, segmentBytes);
     257                    segmentData.addSegment(thisSegmentMarker, segmentBytes);
    224258                }
    225                 // didn't find the one we're looking for, loop through to the next segment
    226259            } while (true);
    227260        } catch (IOException ioe) {
    228             //throw new JpegProcessingException("IOException processing Jpeg file", ioe);
    229261            throw new JpegProcessingException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);
    230262        } finally {
    231263            try {
    232                 if (inStream != null) {
    233                     inStream.close();
     264                if (jpegInputStream != null) {
     265                    jpegInputStream.close();
    234266                }
    235267            } catch (IOException ioe) {
    236                 //throw new JpegProcessingException("IOException processing Jpeg file", ioe);
    237268                throw new JpegProcessingException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);
    238269            }
     
    240271    }
    241272
    242     /**
    243      * Private helper method to create a BufferedInputStream of Jpeg data from whichever
    244      * data source was specified upon construction of this instance.
    245      * @return a a BufferedInputStream of Jpeg data
    246      * @throws JpegProcessingException for any problems obtaining the stream
    247      */
    248     private BufferedInputStream getJpegInputStream() throws JpegProcessingException
    249     {
    250         if (_stream!=null) {
    251             if (_stream instanceof BufferedInputStream) {
    252                 return (BufferedInputStream) _stream;
    253             } else {
    254                 return new BufferedInputStream(_stream);
     273    private boolean checkForBytesOnStream(@NotNull BufferedInputStream stream, int bytesNeeded, boolean waitForBytes) throws IOException
     274    {
     275        // NOTE  waiting is essential for network streams where data can be delayed, but it is not necessary for byte[] or filesystems
     276
     277        if (!waitForBytes)
     278            return bytesNeeded <= stream.available();
     279
     280        int count = 40; // * 100ms = approx 4 seconds
     281        while (count > 0) {
     282            if (bytesNeeded <= stream.available())
     283               return true;
     284            try {
     285                Thread.sleep(100);
     286            } catch (InterruptedException e) {
     287                // continue
    255288            }
     289            count--;
    256290        }
    257         InputStream inputStream;
    258         if (_data == null) {
    259             try {
    260                 inputStream = new FileInputStream(_file);
    261             } catch (FileNotFoundException e) {
    262                 throw new JpegProcessingException("Jpeg file does not exist", e);
    263             }
    264         } else {
    265             inputStream = new ByteArrayInputStream(_data);
    266         }
    267         return new BufferedInputStream(inputStream);
    268     }
    269 
    270     /**
    271      * Helper method that validates the Jpeg file's magic number.
    272      * @param fileStream the InputStream to read bytes from, which must be positioned
    273      *        at its start (i.e. no bytes read yet)
    274      * @return true if the magic number is Jpeg (0xFFD8)
    275      * @throws IOException for any problem in reading the file
    276      */
    277     private boolean isValidJpegHeaderBytes(InputStream fileStream) throws IOException
    278     {
    279         byte[] header = new byte[2];
    280         fileStream.read(header, 0, 2);
    281         return (header[0] & 0xFF) == 0xFF && (header[1] & 0xFF) == 0xD8;
     291        return false;
    282292    }
    283293}
  • trunk/src/com/drew/lang/CompoundException.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.lang;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1625
    1726import java.io.PrintStream;
     
    2231 * unavailable in previous versions.  This class allows support
    2332 * of these previous JDK versions.
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2435 */
    2536public class CompoundException extends Exception
    2637{
    27     private final Throwable _innnerException;
     38    private static final long serialVersionUID = -9207883813472069925L;
    2839
    29     public CompoundException(String msg)
     40    @Nullable
     41    private final Throwable _innerException;
     42
     43    public CompoundException(@Nullable String msg)
    3044    {
    3145        this(msg, null);
    3246    }
    3347
    34     public CompoundException(Throwable exception)
     48    public CompoundException(@Nullable Throwable exception)
    3549    {
    3650        this(null, exception);
    3751    }
    3852
    39     public CompoundException(String msg, Throwable innerException)
     53    public CompoundException(@Nullable String msg, @Nullable Throwable innerException)
    4054    {
    4155        super(msg);
    42         _innnerException = innerException;
     56        _innerException = innerException;
    4357    }
    4458
     59    @Nullable
    4560    public Throwable getInnerException()
    4661    {
    47         return _innnerException;
     62        return _innerException;
    4863    }
    4964
     65    @NotNull
    5066    public String toString()
    5167    {
    52         StringBuffer sbuffer = new StringBuffer();
    53         sbuffer.append(super.toString());
    54         if (_innnerException != null) {
    55             sbuffer.append("\n");
    56             sbuffer.append("--- inner exception ---");
    57             sbuffer.append("\n");
    58             sbuffer.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());
    5975        }
    60         return sbuffer.toString();
     76        return string.toString();
    6177    }
    6278
    63     public void printStackTrace(PrintStream s)
     79    public void printStackTrace(@NotNull PrintStream s)
    6480    {
    6581        super.printStackTrace(s);
    66         if (_innnerException != null) {
     82        if (_innerException != null) {
    6783            s.println("--- inner exception ---");
    68             _innnerException.printStackTrace(s);
     84            _innerException.printStackTrace(s);
    6985        }
    7086    }
    7187
    72     public void printStackTrace(PrintWriter s)
     88    public void printStackTrace(@NotNull PrintWriter s)
    7389    {
    7490        super.printStackTrace(s);
    75         if (_innnerException != null) {
     91        if (_innerException != null) {
    7692            s.println("--- inner exception ---");
    77             _innnerException.printStackTrace(s);
     93            _innerException.printStackTrace(s);
    7894        }
    7995    }
     
    8298    {
    8399        super.printStackTrace();
    84         if (_innnerException != null) {
     100        if (_innerException != null) {
    85101            System.err.println("--- inner exception ---");
    86             _innnerException.printStackTrace();
     102            _innerException.printStackTrace();
    87103        }
    88104    }
  • trunk/src/com/drew/lang/NullOutputStream.java

    r4231 r6127  
    1 /**
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     1/*
     2 * Copyright 2002-2012 Drew Noakes
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.lang;
     
    2024import java.io.OutputStream;
    2125
     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 */
    2231public class NullOutputStream extends OutputStream
    2332{
  • trunk/src/com/drew/lang/Rational.java

    r4231 r6127  
    11/*
    2  * Rational.java
    3  *
    4  * This class is public domain software - that is, you can do whatever you want
    5  * with it, and include it software that is licensed under the GNU or the
    6  * BSD license, or whatever other licence you choose, including proprietary
    7  * closed source licenses.  Similarly, I release this Java version under the
    8  * same license, though I do ask that you leave this header in tact.
    9  *
    10  * If you make modifications to this code that you think would benefit the
    11  * wider community, please send me a copy and I'll post it on my site.
    12  *
    13  * If you make use of this code, I'd appreciate hearing about it.
    14  *   drew.noakes@drewnoakes.com
    15  * Latest version of this software kept at
    16  *   http://drewnoakes.com/
    17  *
    18  * Created on 6 May 2002, 18:06
    19  * Updated 26 Aug 2002 by Drew
    20  * - Added toSimpleString() method, which returns a simplified and hopefully more
    21  *   readable version of the Rational.  i.e. 2/10 -> 1/5, and 10/2 -> 5
    22  * Modified 29 Oct 2002 (v1.2)
    23  * - Improved toSimpleString() to factor more complex rational numbers into
    24  *   a simpler form
    25  *     i.e.
    26  *       10/15 -> 2/3
    27  * - toSimpleString() now accepts a boolean flag, 'allowDecimals' which will
    28  *   display the rational number in decimal form if it fits within 5 digits
    29  *     i.e.
    30  *       3/4 -> 0.75 when allowDecimal == true
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    3120 */
    3221
    3322package com.drew.lang;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
    3426
    3527import java.io.Serializable;
     
    3830 * Immutable class for holding a rational number without loss of precision.  Provides
    3931 * a familiar representation via toString() in form <code>numerator/denominator</code>.
    40  * <p>
    41  * @author  Drew Noakes http://drewnoakes.com
     32 *
     33 * @author Drew Noakes http://drewnoakes.com
    4234 */
    4335public class Rational extends java.lang.Number implements Serializable
    4436{
    45     /**
    46      * Holds the numerator.
    47      */
    48     private final int numerator;
    49 
    50     /**
    51      * Holds the denominator.
    52      */
    53     private final int denominator;
    54 
    55     private int maxSimplificationCalculations = 1000;
     37    private static final long serialVersionUID = 510688928138848770L;
     38
     39    /** Holds the numerator. */
     40    private final long _numerator;
     41
     42    /** Holds the denominator. */
     43    private final long _denominator;
    5644
    5745    /**
     
    6048     * with them!
    6149     */
    62     public Rational(int numerator, int denominator)
    63     {
    64         this.numerator = numerator;
    65         this.denominator = denominator;
     50    public Rational(long numerator, long denominator)
     51    {
     52        _numerator = numerator;
     53        _denominator = denominator;
    6654    }
    6755
     
    7058     * This may involve rounding.
    7159     *
    72      * @return  the numeric value represented by this object after conversion
    73      *          to type <code>double</code>.
     60     * @return the numeric value represented by this object after conversion
     61     *         to type <code>double</code>.
    7462     */
    7563    public double doubleValue()
    7664    {
    77         return (double)numerator / (double)denominator;
     65        return (double) _numerator / (double) _denominator;
    7866    }
    7967
     
    8270     * This may involve rounding.
    8371     *
    84      * @return  the numeric value represented by this object after conversion
    85      *          to type <code>float</code>.
     72     * @return the numeric value represented by this object after conversion
     73     *         to type <code>float</code>.
    8674     */
    8775    public float floatValue()
    8876    {
    89         return (float)numerator / (float)denominator;
     77        return (float) _numerator / (float) _denominator;
    9078    }
    9179
     
    9583     * casts the result of <code>doubleValue()</code> to <code>byte</code>.
    9684     *
    97      * @return  the numeric value represented by this object after conversion
    98      *          to type <code>byte</code>.
     85     * @return the numeric value represented by this object after conversion
     86     *         to type <code>byte</code>.
    9987     */
    10088    public final byte byteValue()
    10189    {
    102         return (byte)doubleValue();
     90        return (byte) doubleValue();
    10391    }
    10492
     
    10896     * casts the result of <code>doubleValue()</code> to <code>int</code>.
    10997     *
    110      * @return  the numeric value represented by this object after conversion
    111      *          to type <code>int</code>.
     98     * @return the numeric value represented by this object after conversion
     99     *         to type <code>int</code>.
    112100     */
    113101    public final int intValue()
    114102    {
    115         return (int)doubleValue();
     103        return (int) doubleValue();
    116104    }
    117105
     
    121109     * casts the result of <code>doubleValue()</code> to <code>long</code>.
    122110     *
    123      * @return  the numeric value represented by this object after conversion
    124      *          to type <code>long</code>.
     111     * @return the numeric value represented by this object after conversion
     112     *         to type <code>long</code>.
    125113     */
    126114    public final long longValue()
    127115    {
    128         return (long)doubleValue();
     116        return (long) doubleValue();
    129117    }
    130118
     
    134122     * casts the result of <code>doubleValue()</code> to <code>short</code>.
    135123     *
    136      * @return  the numeric value represented by this object after conversion
    137      *          to type <code>short</code>.
     124     * @return the numeric value represented by this object after conversion
     125     *         to type <code>short</code>.
    138126     */
    139127    public final short shortValue()
    140128    {
    141         return (short)doubleValue();
    142     }
    143 
    144 
    145     /**
    146      * Returns the denominator.
    147      */
    148     public final int getDenominator()
    149     {
    150         return this.denominator;
    151     }
    152 
    153     /**
    154      * Returns the numerator.
    155      */
    156     public final int getNumerator()
    157     {
    158         return this.numerator;
    159     }
    160 
    161     /**
    162      * Returns the reciprocal value of this obejct as a new Rational.
     129        return (short) doubleValue();
     130    }
     131
     132
     133    /** Returns the denominator. */
     134    public final long getDenominator()
     135    {
     136        return this._denominator;
     137    }
     138
     139    /** Returns the numerator. */
     140    public final long getNumerator()
     141    {
     142        return this._numerator;
     143    }
     144
     145    /**
     146     * Returns the reciprocal value of this object as a new Rational.
     147     *
    163148     * @return the reciprocal in a new object
    164149     */
     150    @NotNull
    165151    public Rational getReciprocal()
    166152    {
    167         return new Rational(this.denominator, this.numerator);
    168     }
    169 
    170     /**
    171      * Checks if this rational number is an Integer, either positive or negative.
    172      */
     153        return new Rational(this._denominator, this._numerator);
     154    }
     155
     156    /** Checks if this rational number is an Integer, either positive or negative. */
    173157    public boolean isInteger()
    174158    {
    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);
    183162    }
    184163
    185164    /**
    186165     * Returns a string representation of the object of form <code>numerator/denominator</code>.
    187      * @return  a string representation of the object.
    188      */
     166     *
     167     * @return a string representation of the object.
     168     */
     169    @NotNull
    189170    public String toString()
    190171    {
    191         return numerator + "/" + denominator;
    192     }
    193 
    194     /**
    195      * Returns the simplest represenation of this Rational's value possible.
    196      */
     172        return _numerator + "/" + _denominator;
     173    }
     174
     175    /** Returns the simplest representation of this Rational's value possible. */
     176    @NotNull
    197177    public String toSimpleString(boolean allowDecimal)
    198178    {
    199         if (denominator == 0 && numerator != 0) {
     179        if (_denominator == 0 && _numerator != 0) {
    200180            return toString();
    201181        } else if (isInteger()) {
    202182            return Integer.toString(intValue());
    203         } else if (numerator != 1 && denominator % numerator == 0) {
     183        } else if (_numerator != 1 && _denominator % _numerator == 0) {
    204184            // common factor between denominator and numerator
    205             int newDenominator = denominator / numerator;
     185            long newDenominator = _denominator / _numerator;
    206186            return new Rational(1, newDenominator).toSimpleString(allowDecimal);
    207187        } else {
     
    220200     * Decides whether a brute-force simplification calculation should be avoided
    221201     * by comparing the maximum number of possible calculations with some threshold.
     202     *
    222203     * @return true if the simplification should be performed, otherwise false
    223204     */
    224205    private boolean tooComplexForSimplification()
    225206    {
    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;
    227209        return maxPossibleCalculations > maxSimplificationCalculations;
    228210    }
     
    231213     * Compares two <code>Rational</code> instances, returning true if they are mathematically
    232214     * equivalent.
     215     *
    233216     * @param obj the Rational to compare this instance to.
    234217     * @return true if instances are mathematically equivalent, otherwise false.  Will also
    235218     *         return false if <code>obj</code> is not an instance of <code>Rational</code>.
    236219     */
    237     public boolean equals(Object obj)
    238     {
    239         if (!(obj instanceof Rational)) {
     220    @Override
     221    public boolean equals(@Nullable Object obj)
     222    {
     223        if (obj==null || !(obj instanceof Rational))
    240224            return false;
    241         }
    242         Rational that = (Rational)obj;
     225        Rational that = (Rational) obj;
    243226        return this.doubleValue() == that.doubleValue();
     227    }
     228
     229    @Override
     230    public int hashCode()
     231    {
     232        return (23 * (int)_denominator) + (int)_numerator;
    244233    }
    245234
     
    252241     * To reduce a rational, need to see if both numerator and denominator are divisible
    253242     * by a common factor.  Using the prime number series in ascending order guarantees
    254      * the minimun number of checks required.</p>
     243     * the minimum number of checks required.</p>
    255244     * <p>
    256245     * However, generating the prime number series seems to be a hefty task.  Perhaps
     
    265254     *   -- * ------------------------------------ + 2
    266255     *   10                    2
    267      *
     256     * <p/>
    268257     *   Math.min(denominator, numerator) - 1
    269258     * = ------------------------------------ + 2
    270259     *                  5
    271260     * </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,
    273263     *         returns itself (unchanged)
    274264     */
     265    @NotNull
    275266    public Rational getSimplifiedInstance()
    276267    {
     
    278269            return this;
    279270        }
    280         for (int factor = 2; factor <= Math.min(denominator, numerator); factor++) {
     271        for (int factor = 2; factor <= Math.min(_denominator, _numerator); factor++) {
    281272            if ((factor % 2 == 0 && factor > 2) || (factor % 5 == 0 && factor > 5)) {
    282273                continue;
    283274            }
    284             if (denominator % factor == 0 && numerator % factor == 0) {
     275            if (_denominator % factor == 0 && _numerator % factor == 0) {
    285276                // found a common factor
    286                 return new Rational(numerator / factor, denominator / factor);
     277                return new Rational(_numerator / factor, _denominator / factor);
    287278            }
    288279        }
  • trunk/src/com/drew/metadata/DefaultTagDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata;
    1822
     23import com.drew.lang.annotations.NotNull;
     24
    1925/**
     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.
    2029 *
     30 * @author Drew Noakes http://drewnoakes.com
    2131 */
    22 public class DefaultTagDescriptor extends TagDescriptor
     32public class DefaultTagDescriptor extends TagDescriptor<Directory>
    2333{
    24     public DefaultTagDescriptor(Directory directory)
     34    public DefaultTagDescriptor(@NotNull Directory directory)
    2535    {
    2636        super(directory);
    2737    }
    2838
     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
    2945    public String getTagName(int tagType)
    3046    {
     
    3349        return "Unknown tag 0x" + hex;
    3450    }
    35 
    36     public String getDescription(int tagType)
    37     {
    38         return _directory.getString(tagType);
    39     }
    4051}
  • trunk/src/com/drew/metadata/Directory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata;
    1822
    1923import com.drew.lang.Rational;
    20 
    21 import java.io.Serializable;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26import com.drew.lang.annotations.SuppressWarnings;
     27
     28import java.io.UnsupportedEncodingException;
    2229import java.lang.reflect.Array;
    2330import java.text.DateFormat;
    24 import java.util.ArrayList;
    25 import java.util.HashMap;
    26 import java.util.Iterator;
    27 import java.util.List;
     31import java.text.ParseException;
     32import java.text.SimpleDateFormat;
     33import java.util.*;
    2834
    2935/**
    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
    3240 */
    33 public abstract class Directory implements Serializable
     41public abstract class Directory
    3442{
    35     /**
    36      * Map of values hashed by type identifiers.
    37      */
    38     protected final HashMap _tagMap;
    39 
    40     /**
    41      * The descriptor used to interperet tag values.
    42      */
    43     protected TagDescriptor _descriptor;
     43    // TODO get Array methods need to return cloned data, to maintain this directory's integrity
     44
     45    /** Map of values hashed by type identifiers. */
     46    @NotNull
     47    protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
    4448
    4549    /**
     
    4852     * defined tags.
    4953     */
    50     protected final List _definedTagList;
    51 
    52     private List _errorList;
     54    @NotNull
     55    protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
     56
     57    @NotNull
     58    private final Collection<String> _errorList = new ArrayList<String>(4);
     59
     60    /** The descriptor used to interpret tag values. */
     61    protected TagDescriptor _descriptor;
    5362
    5463// ABSTRACT METHODS
     
    5665    /**
    5766     * Provides the name of the directory, for display purposes.  E.g. <code>Exif</code>
     67     *
    5868     * @return the name of the directory
    5969     */
     70    @NotNull
    6071    public abstract String getName();
    6172
    6273    /**
    6374     * Provides the map of tag names, hashed by tag type identifier.
     75     *
    6476     * @return the map of tag names
    6577     */
    66     protected abstract HashMap getTagNameMap();
    67 
    68 // CONSTRUCTORS
    69 
    70     /**
    71      * Creates a new Directory.
    72      */
    73     public Directory()
    74     {
    75         _tagMap = new HashMap();
    76         _definedTagList = new ArrayList();
    77     }
     78    @NotNull
     79    protected abstract HashMap<Integer, String> getTagNameMap();
     80
     81    protected Directory()
     82    {}
    7883
    7984// VARIOUS METHODS
     
    8186    /**
    8287     * Indicates whether the specified tag type has been set.
     88     *
    8389     * @param tagType the tag type to check for
    8490     * @return true if a value exists for the specified tag type, false if not
    8591     */
     92    @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
    8693    public boolean containsTag(int tagType)
    8794    {
    88         return _tagMap.containsKey(new Integer(tagType));
     95        return _tagMap.containsKey(Integer.valueOf(tagType));
    8996    }
    9097
    9198    /**
    9299     * Returns an Iterator of Tag instances that have been set in this Directory.
     100     *
    93101     * @return an Iterator of Tag instances
    94102     */
    95     public Iterator getTagIterator()
    96     {
    97         return _definedTagList.iterator();
     103    @NotNull
     104    public Collection<Tag> getTags()
     105    {
     106        return _definedTagList;
    98107    }
    99108
    100109    /**
    101110     * Returns the number of tags set in this Directory.
     111     *
    102112     * @return the number of tags set in this Directory
    103113     */
     
    108118
    109119    /**
    110      * Sets the descriptor used to interperet tag values.
    111      * @param descriptor the descriptor used to interperet tag values
    112      */
    113     public void setDescriptor(TagDescriptor descriptor)
    114     {
    115         if (descriptor==null) {
     120     * Sets the descriptor used to interpret tag values.
     121     *
     122     * @param descriptor the descriptor used to interpret tag values
     123     */
     124    @java.lang.SuppressWarnings({ "ConstantConditions" })
     125    public void setDescriptor(@NotNull TagDescriptor descriptor)
     126    {
     127        if (descriptor == null)
    116128            throw new NullPointerException("cannot set a null descriptor");
    117         }
    118129        _descriptor = descriptor;
    119130    }
    120131
    121     public void addError(String message)
    122     {
    123         if (_errorList==null) {
    124             _errorList = new ArrayList();
    125         }
     132    /**
     133     * Registers an error message with this directory.
     134     *
     135     * @param message an error message.
     136     */
     137    public void addError(@NotNull String message)
     138    {
    126139        _errorList.add(message);
    127140    }
    128141
     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     */
    129147    public boolean hasErrors()
    130148    {
    131         return (_errorList!=null && _errorList.size()>0);
    132     }
    133 
    134     public Iterator getErrors()
    135     {
    136         return _errorList.iterator();
    137     }
    138 
     149        return _errorList.size() > 0;
     150    }
     151
     152    /**
     153     * Used to iterate over any error messages contained in this directory.
     154     *
     155     * @return an iterable collection of error message strings.
     156     */
     157    @NotNull
     158    public Iterable<String> getErrors()
     159    {
     160        return _errorList;
     161    }
     162
     163    /** Returns the count of error messages in this directory. */
    139164    public int getErrorCount()
    140165    {
     
    145170
    146171    /**
    147      * Sets an int value for the specified tag.
     172     * Sets an <code>int</code> value for the specified tag.
     173     *
    148174     * @param tagType the tag's value as an int
    149      * @param value the value for the specified tag as an int
     175     * @param value   the value for the specified tag as an int
    150176     */
    151177    public void setInt(int tagType, int value)
    152178    {
    153         setObject(tagType, new Integer(value));
    154     }
    155 
    156     /**
    157      * Sets a double value for the specified tag.
     179        setObject(tagType, value);
     180    }
     181
     182    /**
     183     * Sets an <code>int[]</code> (array) for the specified tag.
     184     *
     185     * @param tagType the tag identifier
     186     * @param ints    the int array to store
     187     */
     188    public void setIntArray(int tagType, @NotNull int[] ints)
     189    {
     190        setObjectArray(tagType, ints);
     191    }
     192
     193    /**
     194     * Sets a <code>float</code> value for the specified tag.
     195     *
    158196     * @param tagType the tag's value as an int
    159      * @param value the value for the specified tag as a double
     197     * @param value   the value for the specified tag as a float
     198     */
     199    public void setFloat(int tagType, float value)
     200    {
     201        setObject(tagType, value);
     202    }
     203
     204    /**
     205     * Sets a <code>float[]</code> (array) for the specified tag.
     206     *
     207     * @param tagType the tag identifier
     208     * @param floats  the float array to store
     209     */
     210    public void setFloatArray(int tagType, @NotNull float[] floats)
     211    {
     212        setObjectArray(tagType, floats);
     213    }
     214
     215    /**
     216     * Sets a <code>double</code> value for the specified tag.
     217     *
     218     * @param tagType the tag's value as an int
     219     * @param value   the value for the specified tag as a double
    160220     */
    161221    public void setDouble(int tagType, double value)
    162222    {
    163         setObject(tagType, new Double(value));
    164     }
    165 
    166     /**
    167      * Sets a float value for the specified tag.
     223        setObject(tagType, value);
     224    }
     225
     226    /**
     227     * Sets a <code>double[]</code> (array) for the specified tag.
     228     *
     229     * @param tagType the tag identifier
     230     * @param doubles the double array to store
     231     */
     232    public void setDoubleArray(int tagType, @NotNull double[] doubles)
     233    {
     234        setObjectArray(tagType, doubles);
     235    }
     236
     237    /**
     238     * Sets a <code>String</code> value for the specified tag.
     239     *
    168240     * @param tagType the tag's value as an int
    169      * @param value the value for the specified tag as a float
    170      */
    171     public void setFloat(int tagType, float value)
    172     {
    173         setObject(tagType, new Float(value));
    174     }
    175 
    176     /**
    177      * Sets an int value for the specified tag.
    178      * @param tagType the tag's value as an int
    179      * @param value the value for the specified tag as a String
    180      */
    181     public void setString(int tagType, String value)
    182     {
     241     * @param value   the value for the specified tag as a String
     242     */
     243    @java.lang.SuppressWarnings({ "ConstantConditions" })
     244    public void setString(int tagType, @NotNull String value)
     245    {
     246        if (value == null)
     247            throw new NullPointerException("cannot set a null String");
    183248        setObject(tagType, value);
    184249    }
    185250
    186251    /**
    187      * Sets an int value for the specified tag.
    188      * @param tagType the tag's value as an int
    189      * @param value the value for the specified tag as a boolean
    190      */
    191     public void setBoolean(int tagType, boolean value)
    192     {
    193         setObject(tagType, new Boolean(value));
    194     }
    195 
    196     /**
    197      * Sets a long value for the specified tag.
    198      * @param tagType the tag's value as an int
    199      * @param value the value for the specified tag as a long
    200      */
    201     public void setLong(int tagType, long value)
    202     {
    203         setObject(tagType, new Long(value));
    204     }
    205 
    206     /**
    207      * Sets a java.util.Date value for the specified tag.
    208      * @param tagType the tag's value as an int
    209      * @param value the value for the specified tag as a java.util.Date
    210      */
    211     public void setDate(int tagType, java.util.Date value)
    212     {
    213         setObject(tagType, value);
    214     }
    215 
    216     /**
    217      * Sets a Rational value for the specified tag.
    218      * @param tagType the tag's value as an int
    219      * @param rational rational number
    220      */
    221     public void setRational(int tagType, Rational rational)
    222     {
    223         setObject(tagType, rational);
    224     }
    225 
    226     /**
    227      * Sets a Rational array for the specified tag.
    228      * @param tagType the tag identifier
    229      * @param rationals the Rational array to store
    230      */
    231     public void setRationalArray(int tagType, Rational[] rationals)
    232     {
    233         setObjectArray(tagType, rationals);
    234     }
    235 
    236     /**
    237      * Sets an int array for the specified tag.
    238      * @param tagType the tag identifier
    239      * @param ints the int array to store
    240      */
    241     public void setIntArray(int tagType, int[] ints)
    242     {
    243         setObjectArray(tagType, ints);
    244     }
    245 
    246     /**
    247      * Sets a byte array for the specified tag.
    248      * @param tagType the tag identifier
    249      * @param bytes the byte array to store
    250      */
    251     public void setByteArray(int tagType, byte[] bytes)
    252     {
    253         setObjectArray(tagType, bytes);
    254     }
    255 
    256     /**
    257      * Sets a String array for the specified tag.
     252     * Sets a <code>String[]</code> (array) for the specified tag.
     253     *
    258254     * @param tagType the tag identifier
    259255     * @param strings the String array to store
    260256     */
    261     public void setStringArray(int tagType, String[] strings)
     257    public void setStringArray(int tagType, @NotNull String[] strings)
    262258    {
    263259        setObjectArray(tagType, strings);
     
    265261
    266262    /**
    267      * Private helper method, containing common functionality for all 'add'
    268      * methods.
     263     * Sets a <code>boolean</code> value for the specified tag.
     264     *
    269265     * @param tagType the tag's value as an int
    270      * @param value the value for the specified tag
     266     * @param value   the value for the specified tag as a boolean
     267     */
     268    public void setBoolean(int tagType, boolean value)
     269    {
     270        setObject(tagType, value);
     271    }
     272
     273    /**
     274     * Sets a <code>long</code> value for the specified tag.
     275     *
     276     * @param tagType the tag's value as an int
     277     * @param value   the value for the specified tag as a long
     278     */
     279    public void setLong(int tagType, long value)
     280    {
     281        setObject(tagType, value);
     282    }
     283
     284    /**
     285     * Sets a <code>java.util.Date</code> value for the specified tag.
     286     *
     287     * @param tagType the tag's value as an int
     288     * @param value   the value for the specified tag as a java.util.Date
     289     */
     290    public void setDate(int tagType, @NotNull java.util.Date value)
     291    {
     292        setObject(tagType, value);
     293    }
     294
     295    /**
     296     * Sets a <code>Rational</code> value for the specified tag.
     297     *
     298     * @param tagType  the tag's value as an int
     299     * @param rational rational number
     300     */
     301    public void setRational(int tagType, @NotNull Rational rational)
     302    {
     303        setObject(tagType, rational);
     304    }
     305
     306    /**
     307     * Sets a <code>Rational[]</code> (array) for the specified tag.
     308     *
     309     * @param tagType   the tag identifier
     310     * @param rationals the Rational array to store
     311     */
     312    public void setRationalArray(int tagType, @NotNull Rational[] rationals)
     313    {
     314        setObjectArray(tagType, rationals);
     315    }
     316
     317    /**
     318     * Sets a <code>byte[]</code> (array) for the specified tag.
     319     *
     320     * @param tagType the tag identifier
     321     * @param bytes   the byte array to store
     322     */
     323    public void setByteArray(int tagType, @NotNull byte[] bytes)
     324    {
     325        setObjectArray(tagType, bytes);
     326    }
     327
     328    /**
     329     * Sets a <code>Object</code> for the specified tag.
     330     *
     331     * @param tagType the tag's value as an int
     332     * @param value   the value for the specified tag
    271333     * @throws NullPointerException if value is <code>null</code>
    272334     */
    273     public void setObject(int tagType, Object value)
    274     {
    275         if (value==null) {
     335    @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
     336    public void setObject(int tagType, @NotNull Object value)
     337    {
     338        if (value == null)
    276339            throw new NullPointerException("cannot set a null object");
    277         }
    278 
    279         Integer key = new Integer(tagType);
    280         if (!_tagMap.containsKey(key)) {
     340
     341        if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
    281342            _definedTagList.add(new Tag(tagType, this));
    282343        }
    283         _tagMap.put(key, value);
    284     }
    285 
    286     /**
    287      * Private helper method, containing common functionality for all 'add...Array'
    288      * methods.
     344//        else {
     345//            final Object oldValue = _tagMap.get(tagType);
     346//            if (!oldValue.equals(value))
     347//                addError(String.format("Overwritten tag 0x%s (%s).  Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value));
     348//        }
     349        _tagMap.put(tagType, value);
     350    }
     351
     352    /**
     353     * Sets an array <code>Object</code> for the specified tag.
     354     *
    289355     * @param tagType the tag's value as an int
    290      * @param array the array of values for the specified tag
    291      */
    292     public void setObjectArray(int tagType, Object array)
     356     * @param array   the array of values for the specified tag
     357     */
     358    public void setObjectArray(int tagType, @NotNull Object array)
    293359    {
    294360        // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles
     
    299365
    300366    /**
    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.
    302380     */
    303381    public int getInt(int tagType) throws MetadataException
    304382    {
    305         Object o = getObject(tagType);
    306         if (o==null) {
    307             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    308         } else if (o instanceof String) {
     383        Integer integer = getInteger(tagType);
     384        if (integer!=null)
     385            return integer;
     386
     387        Object o = getObject(tagType);
     388        if (o == null)
     389            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     390        throw new MetadataException("Tag '" + tagType + "' cannot be converted to int.  It is of type '" + o.getClass() + "'.");
     391    }
     392
     393    /**
     394     * Returns the specified tag's value as an Integer, if possible.  Every attempt to represent the tag's value as an
     395     * Integer is taken.  Here is a list of the action taken depending upon the tag's original type:
     396     * <ul>
     397     * <li> int - Return unchanged
     398     * <li> Number - Return an int value (real numbers are truncated)
     399     * <li> Rational - Truncate any fractional part and returns remaining int
     400     * <li> String - Attempt to parse string as an int.  If this fails, convert the char[] to an int (using shifts and OR)
     401     * <li> Rational[] - Return int value of first item in array if length &gt; 0
     402     * <li> byte[] - Return int value of first item in array if length &gt; 0
     403     * <li> int[] - Return int value of first item in array if length &gt; 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) {
    309417            try {
    310418                return Integer.parseInt((String)o);
     
    314422                byte[] bytes = s.getBytes();
    315423                long val = 0;
    316                 for (int i = 0; i < bytes.length; i++) {
     424                for (byte aByte : bytes) {
    317425                    val = val << 8;
    318                     val += bytes[i];
     426                    val += (aByte & 0xff);
    319427                }
    320428                return (int)val;
     
    324432        } else if (o instanceof Rational[]) {
    325433            Rational[] rationals = (Rational[])o;
    326             if (rationals.length==1)
     434            if (rationals.length == 1)
    327435                return rationals[0].intValue();
    328436        } else if (o instanceof byte[]) {
    329437            byte[] bytes = (byte[])o;
    330             if (bytes.length==1)
    331                 return bytes[0];
     438            if (bytes.length == 1)
     439                return (int)bytes[0];
    332440        } else if (o instanceof int[]) {
    333441            int[] ints = (int[])o;
    334             if (ints.length==1)
     442            if (ints.length == 1)
    335443                return ints[0];
    336444        }
    337         throw new MetadataException("Tag '" + tagType + "' cannot be cast to int.  It is of type '" + o.getClass() + "'.");
    338     }
    339 
    340     // TODO get Array methods need to return cloned data, to maintain this directory's integrity
     445        return null;
     446    }
    341447
    342448    /**
    343449     * Gets the specified tag's value as a String array, if possible.  Only supported
    344450     * where the tag is set as String[], String, int[], byte[] or Rational[].
     451     *
    345452     * @param tagType the tag identifier
    346      * @return the tag's value as an array of Strings
    347      * @throws MetadataException if the tag has not been set or cannot be represented
    348      *         as a String[]
    349      */
    350     public String[] getStringArray(int tagType) throws MetadataException
    351     {
    352         Object o = getObject(tagType);
    353         if (o==null) {
    354             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    355         } else if (o instanceof String[]) {
     453     * @return the tag's value as an array of Strings. If the value is unset or cannot be converted, <code>null</code> is returned.
     454     */
     455    @Nullable
     456    public String[] getStringArray(int tagType)
     457    {
     458        Object o = getObject(tagType);
     459        if (o == null)
     460            return null;
     461        if (o instanceof String[])
    356462            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[]) {
    361466            int[] ints = (int[])o;
    362467            String[] strings = new String[ints.length];
    363             for (int i = 0; i<strings.length; i++) {
     468            for (int i = 0; i < strings.length; i++)
    364469                strings[i] = Integer.toString(ints[i]);
    365             }
    366470            return strings;
    367471        } else if (o instanceof byte[]) {
    368472            byte[] bytes = (byte[])o;
    369473            String[] strings = new String[bytes.length];
    370             for (int i = 0; i<strings.length; i++) {
     474            for (int i = 0; i < strings.length; i++)
    371475                strings[i] = Byte.toString(bytes[i]);
    372             }
    373476            return strings;
    374477        } else if (o instanceof Rational[]) {
    375478            Rational[] rationals = (Rational[])o;
    376479            String[] strings = new String[rationals.length];
    377             for (int i = 0; i<strings.length; i++) {
     480            for (int i = 0; i < strings.length; i++)
    378481                strings[i] = rationals[i].toSimpleString(false);
    379             }
    380482            return strings;
    381483        }
    382         throw new MetadataException("Tag '" + tagType + "' cannot be cast to an String array.  It is of type '" + o.getClass() + "'.");
     484        return null;
    383485    }
    384486
    385487    /**
    386488     * 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     *
    388491     * @param tagType the tag identifier
    389492     * @return the tag's value as an int array
    390      * @throws MetadataException if the tag has not been set, or cannot be converted to
    391      *         an int array
    392      */
    393     public int[] getIntArray(int tagType) throws MetadataException
    394     {
    395         Object o = getObject(tagType);
    396         if (o==null) {
    397             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    398         } else if (o instanceof Rational[]) {
     493     */
     494    @Nullable
     495    public int[] getIntArray(int tagType)
     496    {
     497        Object o = getObject(tagType);
     498        if (o == null)
     499            return null;
     500        if (o instanceof Rational[]) {
    399501            Rational[] rationals = (Rational[])o;
    400502            int[] ints = new int[rationals.length];
    401             for (int i = 0; i<ints.length; i++) {
     503            for (int i = 0; i < ints.length; i++) {
    402504                ints[i] = rationals[i].intValue();
    403505            }
    404506            return ints;
    405         } else if (o instanceof int[]) {
     507        }
     508        if (o instanceof int[])
    406509            return (int[])o;
    407         } else if (o instanceof byte[]) {
     510        if (o instanceof byte[]) {
    408511            byte[] bytes = (byte[])o;
    409512            int[] ints = new int[bytes.length];
    410             for (int i = 0; i<bytes.length; i++) {
     513            for (int i = 0; i < bytes.length; i++) {
    411514                byte b = bytes[i];
    412515                ints[i] = b;
    413516            }
    414517            return ints;
    415         } else if (o instanceof String) {
    416             String str = (String)o;
     518        }
     519        if (o instanceof CharSequence) {
     520            CharSequence str = (CharSequence)o;
    417521            int[] ints = new int[str.length()];
    418             for (int i = 0; i<str.length(); i++) {
     522            for (int i = 0; i < str.length(); i++) {
    419523                ints[i] = str.charAt(i);
    420524            }
    421525            return ints;
    422526        }
    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;
    424531    }
    425532
    426533    /**
    427534     * 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     *
    429537     * @param tagType the tag identifier
    430538     * @return the tag's value as a byte array
    431      * @throws MetadataException if the tag has not been set, or cannot be converted to
    432      *         a byte array
    433      */
    434     public byte[] getByteArray(int tagType) throws MetadataException
    435     {
    436         Object o = getObject(tagType);
    437         if (o==null) {
    438             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
     539     */
     540    @Nullable
     541    public byte[] getByteArray(int tagType)
     542    {
     543        Object o = getObject(tagType);
     544        if (o == null) {
     545            return null;
    439546        } else if (o instanceof Rational[]) {
    440547            Rational[] rationals = (Rational[])o;
    441548            byte[] bytes = new byte[rationals.length];
    442             for (int i = 0; i<bytes.length; i++) {
     549            for (int i = 0; i < bytes.length; i++) {
    443550                bytes[i] = rationals[i].byteValue();
    444551            }
     
    449556            int[] ints = (int[])o;
    450557            byte[] bytes = new byte[ints.length];
    451             for (int i = 0; i<ints.length; i++) {
     558            for (int i = 0; i < ints.length; i++) {
    452559                bytes[i] = (byte)ints[i];
    453560            }
    454561            return bytes;
    455         } else if (o instanceof String) {
    456             String str = (String)o;
     562        } else if (o instanceof CharSequence) {
     563            CharSequence str = (CharSequence)o;
    457564            byte[] bytes = new byte[str.length()];
    458             for (int i = 0; i<str.length(); i++) {
     565            for (int i = 0; i < str.length(); i++) {
    459566                bytes[i] = (byte)str.charAt(i);
    460567            }
    461568            return bytes;
    462569        }
    463         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a byte array.  It is of type '" + o.getClass() + "'.");
    464     }
    465 
    466     /**
    467      * Returns the specified tag's value as a double, if possible.
    468      */
     570        if (o instanceof Integer)
     571            return new byte[] { ((Integer)o).byteValue() };
     572
     573        return null;
     574    }
     575
     576    /** Returns the specified tag's value as a double, if possible. */
    469577    public double getDouble(int tagType) throws MetadataException
    470578    {
    471         Object o = getObject(tagType);
    472         if (o==null) {
    473             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    474         } else if (o instanceof String) {
     579        Double value = getDoubleObject(tagType);
     580        if (value!=null)
     581            return value;
     582        Object o = getObject(tagType);
     583        if (o == null)
     584            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     585        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double.  It is of type '" + o.getClass() + "'.");
     586    }
     587    /** Returns the specified tag's value as a Double.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     588    @Nullable
     589    public Double getDoubleObject(int tagType)
     590    {
     591        Object o = getObject(tagType);
     592        if (o == null)
     593            return null;
     594        if (o instanceof String) {
    475595            try {
    476596                return Double.parseDouble((String)o);
    477597            } catch (NumberFormatException nfe) {
    478                 throw new MetadataException("unable to parse string " + o + " as a double", nfe);
    479             }
    480         } else if (o instanceof Number) {
     598                return null;
     599            }
     600        }
     601        if (o instanceof Number)
    481602            return ((Number)o).doubleValue();
    482         }
    483         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a double.  It is of type '" + o.getClass() + "'.");
    484     }
    485 
    486     /**
    487      * Returns the specified tag's value as a float, if possible.
    488      */
     603
     604        return null;
     605    }
     606
     607    /** Returns the specified tag's value as a float, if possible. */
    489608    public float getFloat(int tagType) throws MetadataException
    490609    {
    491         Object o = getObject(tagType);
    492         if (o==null) {
    493             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    494         } else if (o instanceof String) {
     610        Float value = getFloatObject(tagType);
     611        if (value!=null)
     612            return value;
     613        Object o = getObject(tagType);
     614        if (o == null)
     615            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     616        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float.  It is of type '" + o.getClass() + "'.");
     617    }
     618
     619    /** Returns the specified tag's value as a float.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     620    @Nullable
     621    public Float getFloatObject(int tagType)
     622    {
     623        Object o = getObject(tagType);
     624        if (o == null)
     625            return null;
     626        if (o instanceof String) {
    495627            try {
    496628                return Float.parseFloat((String)o);
    497629            } catch (NumberFormatException nfe) {
    498                 throw new MetadataException("unable to parse string " + o + " as a float", nfe);
    499             }
    500         } else if (o instanceof Number) {
     630                return null;
     631            }
     632        }
     633        if (o instanceof Number)
    501634            return ((Number)o).floatValue();
    502         }
    503         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a float.  It is of type '" + o.getClass() + "'.");
    504     }
    505 
    506     /**
    507      * Returns the specified tag's value as a long, if possible.
    508      */
     635        return null;
     636    }
     637
     638    /** Returns the specified tag's value as a long, if possible. */
    509639    public long getLong(int tagType) throws MetadataException
    510640    {
    511         Object o = getObject(tagType);
    512         if (o==null) {
    513             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    514         } else if (o instanceof String) {
     641        Long value = getLongObject(tagType);
     642        if (value!=null)
     643            return value;
     644        Object o = getObject(tagType);
     645        if (o == null)
     646            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     647        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long.  It is of type '" + o.getClass() + "'.");
     648    }
     649
     650    /** Returns the specified tag's value as a long.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     651    @Nullable
     652    public Long getLongObject(int tagType)
     653    {
     654        Object o = getObject(tagType);
     655        if (o == null)
     656            return null;
     657        if (o instanceof String) {
    515658            try {
    516659                return Long.parseLong((String)o);
    517660            } catch (NumberFormatException nfe) {
    518                 throw new MetadataException("unable to parse string " + o + " as a long", nfe);
    519             }
    520         } else if (o instanceof Number) {
     661                return null;
     662            }
     663        }
     664        if (o instanceof Number)
    521665            return ((Number)o).longValue();
    522         }
    523         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a long.  It is of type '" + o.getClass() + "'.");
    524     }
    525 
    526     /**
    527      * Returns the specified tag's value as a boolean, if possible.
    528      */
     666        return null;
     667    }
     668
     669    /** Returns the specified tag's value as a boolean, if possible. */
    529670    public boolean getBoolean(int tagType) throws MetadataException
    530671    {
    531         Object o = getObject(tagType);
    532         if (o==null) {
    533             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    534         } else if (o instanceof Boolean) {
    535             return ((Boolean)o).booleanValue();
    536         } else if (o instanceof String) {
     672        Boolean value = getBooleanObject(tagType);
     673        if (value!=null)
     674            return value;
     675        Object o = getObject(tagType);
     676        if (o == null)
     677            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     678        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean.  It is of type '" + o.getClass() + "'.");
     679    }
     680
     681    /** Returns the specified tag's value as a boolean.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     682    @Nullable
     683    @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
     684    public Boolean getBooleanObject(int tagType)
     685    {
     686        Object o = getObject(tagType);
     687        if (o == null)
     688            return null;
     689        if (o instanceof Boolean)
     690            return (Boolean)o;
     691        if (o instanceof String) {
    537692            try {
    538693                return Boolean.getBoolean((String)o);
    539694            } catch (NumberFormatException nfe) {
    540                 throw new MetadataException("unable to parse string " + o + " as a boolean", nfe);
    541             }
    542         } else if (o instanceof Number) {
    543             return (((Number)o).doubleValue()!=0);
    544         }
    545         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean.  It is of type '" + o.getClass() + "'.");
    546     }
    547 
    548     /**
    549      * Returns the specified tag's value as a java.util.Date, if possible.
    550      */
    551     public java.util.Date getDate(int tagType) throws MetadataException
    552     {
    553         Object o = getObject(tagType);
    554         if (o==null) {
    555             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    556         } else if (o instanceof java.util.Date) {
     695                return null;
     696            }
     697        }
     698        if (o instanceof Number)
     699            return (((Number)o).doubleValue() != 0);
     700        return null;
     701    }
     702
     703    /**
     704     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
     705     * <p/>
     706     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
     707     * the current {@link TimeZone}.  If the {@link TimeZone} is known, call the overload that accepts one as an argument.
     708     */
     709    @Nullable
     710    public java.util.Date getDate(int tagType)
     711    {
     712        return getDate(tagType, null);
     713    }
     714   
     715    /**
     716     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
     717     * <p/>
     718     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
     719     * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).  Note that this parameter
     720     * is only considered if the underlying value is a string and parsing occurs, otherwise it has no effect.
     721     */
     722    @Nullable
     723    public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone)
     724    {
     725        Object o = getObject(tagType);
     726
     727        if (o == null)
     728            return null;
     729
     730        if (o instanceof java.util.Date)
    557731            return (java.util.Date)o;
    558         } else if (o instanceof String) {
    559             // add new dateformat strings to make this method even smarter
    560             // so far, this seems to cover all known date strings
    561             // (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
    562736            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" };
    567743            String dateString = (String)o;
    568             for (int i = 0; i<datePatterns.length; i++) {
     744            for (String datePattern : datePatterns) {
    569745                try {
    570                     DateFormat parser = new java.text.SimpleDateFormat(datePatterns[i]);
     746                    DateFormat parser = new SimpleDateFormat(datePattern);
     747                    if (timeZone != null)
     748                        parser.setTimeZone(timeZone);
    571749                    return parser.parse(dateString);
    572                 } catch (java.text.ParseException ex) {
     750                } catch (ParseException ex) {
    573751                    // simply try the next pattern
    574752                }
    575753            }
    576754        }
    577         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a java.util.Date.  It is of type '" + o.getClass() + "'.");
    578     }
    579 
    580     /**
    581      * Returns the specified tag's value as a Rational, if possible.
    582      */
    583     public Rational getRational(int tagType) throws MetadataException
    584     {
    585         Object o = getObject(tagType);
    586         if (o==null) {
    587             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    588         } else if (o instanceof Rational) {
     755        return null;
     756    }
     757
     758    /** Returns the specified tag's value as a Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
     759    @Nullable
     760    public Rational getRational(int tagType)
     761    {
     762        Object o = getObject(tagType);
     763
     764        if (o == null)
     765            return null;
     766
     767        if (o instanceof Rational)
    589768            return (Rational)o;
    590         }
    591         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational.  It is of type '" + o.getClass() + "'.");
    592     }
    593 
    594     public Rational[] getRationalArray(int tagType) throws MetadataException
    595     {
    596         Object o = getObject(tagType);
    597         if (o==null) {
    598             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    599         } else if (o instanceof Rational[]) {
     769        if (o instanceof Integer)
     770            return new Rational((Integer)o, 1);
     771        if (o instanceof Long)
     772            return new Rational((Long)o, 1);
     773
     774        // NOTE not doing conversions for real number types
     775
     776        return null;
     777    }
     778
     779    /** Returns the specified tag's value as an array of Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
     780    @Nullable
     781    public Rational[] getRationalArray(int tagType)
     782    {
     783        Object o = getObject(tagType);
     784        if (o == null)
     785            return null;
     786
     787        if (o instanceof Rational[])
    600788            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;
    603791    }
    604792
     
    606794     * Returns the specified tag's value as a String.  This value is the 'raw' value.  A more presentable decoding
    607795     * 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
    609798     *         <code>null</code> if the tag hasn't been defined.
    610799     */
     800    @Nullable
    611801    public String getString(int tagType)
    612802    {
    613803        Object o = getObject(tagType);
    614         if (o==null)
     804        if (o == null)
    615805            return null;
    616806
     
    618808            return ((Rational)o).toSimpleString(true);
    619809
    620         if (o.getClass().isArray())
    621         {
     810        if (o.getClass().isArray()) {
    622811            // handle arrays of objects and primitives
    623812            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(' ');
    631824                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));
    633836                else
    634                     sbuffer.append(Array.getInt(o, i));
    635             }
    636             return sbuffer.toString();
    637         }
    638 
     837                    addError("Unexpected array component type: " + componentType.getName());
     838            }
     839            return string.toString();
     840        }
     841
     842        // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
     843        // the actual data within the file.  It is not inconceivable that whitespace may be significant here, so we
     844        // do not trim.  Also, if support is added for writing data back to files, this may cause issues.
     845        // We leave trimming to the presentation layer.
    639846        return o.toString();
    640847    }
    641848
     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
    642862    /**
    643863     * Returns the object hashed for the particular tag type specified, if available.
     864     *
    644865     * @param tagType the tag type identifier
    645      * @return the tag's value as an Object if available, else null
    646      */
     866     * @return the tag's value as an Object if available, else <code>null</code>
     867     */
     868    @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
     869    @Nullable
    647870    public Object getObject(int tagType)
    648871    {
    649         return _tagMap.get(new Integer(tagType));
     872        return _tagMap.get(Integer.valueOf(tagType));
    650873    }
    651874
     
    654877    /**
    655878     * Returns the name of a specified tag as a String.
     879     *
    656880     * @param tagType the tag type identifier
    657881     * @return the tag's name as a String
    658882     */
     883    @NotNull
    659884    public String getTagName(int tagType)
    660885    {
    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)) {
    664888            String hex = Integer.toHexString(tagType);
    665             while (hex.length()<4) {
     889            while (hex.length() < 4) {
    666890                hex = "0" + hex;
    667891            }
    668892            return "Unknown tag (0x" + hex + ")";
    669893        }
    670         return (String)nameMap.get(key);
     894        return nameMap.get(tagType);
    671895    }
    672896
     
    674898     * Provides a description of a tag's value using the descriptor set by
    675899     * <code>setDescriptor(Descriptor)</code>.
     900     *
    676901     * @param tagType the tag type identifier
    677902     * @return the tag value's description as a String
    678      * @throws MetadataException if a descriptor hasn't been set, or if an error
    679      * occurs during calculation of the description within the Descriptor
    680      */
    681     public String getDescription(int tagType) throws MetadataException
    682     {
    683         if (_descriptor==null) {
    684             throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided");
    685         }
    686 
     903     */
     904    @Nullable
     905    public String getDescription(int tagType)
     906    {
     907        assert(_descriptor != null);
    687908        return _descriptor.getDescription(tagType);
    688909    }
  • trunk/src/com/drew/metadata/Metadata.java

    r4231 r6127  
    11/*
    2  * Metadata.java
     2 * Copyright 2002-2012 Drew Noakes
    33 *
    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
    97 *
    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
    129 *
    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.
    1715 *
    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/
    2820 */
    2921package com.drew.metadata;
    3022
    31 import java.io.Serializable;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25
    3226import java.util.ArrayList;
     27import java.util.Collection;
    3328import java.util.HashMap;
    34 import java.util.Iterator;
     29import java.util.Map;
    3530
    3631/**
    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
    4039 */
    41 public final class Metadata implements Serializable
     40public final class Metadata
    4241{
    43     /**
    44      *
    45      */
    46     private final HashMap directoryMap;
    47 
     42    @NotNull
     43    private final Map<Class<? extends Directory>,Directory> _directoryByClass = new HashMap<Class<? extends Directory>, Directory>();
     44   
    4845    /**
    4946     * List of Directory objects set against this object.  Keeping a list handy makes
    5047     * creation of an Iterator and counting tags simple.
    5148     */
    52     private final ArrayList directoryList;
     49    @NotNull
     50    private final Collection<Directory> _directoryList = new ArrayList<Directory>();
    5351
    5452    /**
    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
    5656     */
    57     public Metadata()
     57    @NotNull
     58    public Iterable<Directory> getDirectories()
    5859    {
    59         directoryMap = new HashMap();
    60         directoryList = new ArrayList();
    61     }
    62 
    63 
    64 // OTHER METHODS
    65 
    66     /**
    67      * Creates an Iterator over the tag types set against this image, preserving the order
    68      * in which they were set.  Should the same tag have been set more than once, it's first
    69      * position is maintained, even though the final value is used.
    70      * @return an Iterator of tag types set for this image
    71      */
    72     public Iterator getDirectoryIterator()
    73     {
    74         return directoryList.iterator();
     60        return _directoryList;
    7561    }
    7662
    7763    /**
    7864     * Returns a count of unique directories in this metadata collection.
     65     *
    7966     * @return the number of unique directory types set for this metadata collection
    8067     */
    8168    public int getDirectoryCount()
    8269    {
    83         return directoryList.size();
     70        return _directoryList.size();
    8471    }
    8572
     
    8875     * such a directory, it is returned.  Otherwise a new instance of this directory will be created and stored within
    8976     * this Metadata object.
     77     *
    9078     * @param type the type of the Directory implementation required.
    9179     * @return a directory of the specified type.
    9280     */
    93     public Directory getDirectory(Class type)
     81    @NotNull
     82    @SuppressWarnings("unchecked")
     83    public <T extends Directory> T getOrCreateDirectory(@NotNull Class<T> type)
    9484    {
    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
    9888        // check if we've already issued this type of directory
    99         if (directoryMap.containsKey(type)) {
    100             return (Directory)directoryMap.get(type);
    101         }
    102         Object directory;
     89        if (_directoryByClass.containsKey(type))
     90            return (T)_directoryByClass.get(type);
     91
     92        T directory;
    10393        try {
    10494            directory = type.newInstance();
     
    10696            throw new RuntimeException("Cannot instantiate provided Directory type: " + type.toString());
    10797        }
    108         // store the directory in case it's requested later
    109         directoryMap.put(type, directory);
    110         directoryList.add(directory);
    111         return (Directory)directory;
     98        // store the directory
     99        _directoryByClass.put(type, directory);
     100        _directoryList.add(directory);
     101
     102        return directory;
     103    }
     104
     105    /**
     106     * If this <code>Metadata</code> object contains a <code>Directory</code> of the specified type, it is returned.
     107     * Otherwise <code>null</code> is returned.
     108     *
     109     * @param type the Directory type
     110     * @param <T> the Directory type
     111     * @return a Directory of type T if it exists in this Metadata object, otherwise <code>null</code>.
     112     */
     113    @Nullable
     114    @SuppressWarnings("unchecked")
     115    public <T extends Directory> T getDirectory(@NotNull Class<T> type)
     116    {
     117        // We suppress the warning here as the code asserts a map signature of Class<T>,T.
     118        // So after get(Class<T>) it is for sure the result is from type T.
     119
     120        return (T)_directoryByClass.get(type);
    112121    }
    113122
    114123    /**
    115124     * 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     *
    117127     * @param type the Directory type
    118128     * @return true if the metadata directory has been created
    119129     */
    120     public boolean containsDirectory(Class type)
     130    public boolean containsDirectory(Class<? extends Directory> type)
    121131    {
    122         return directoryMap.containsKey(type);
     132        return _directoryByClass.containsKey(type);
     133    }
     134
     135    /**
     136     * Indicates whether any errors were reported during the reading of metadata values.
     137     * This value will be true if Directory.hasErrors() is true for one of the contained Directory objects.
     138     *
     139     * @return whether one of the contained directories has an error
     140     */
     141    public boolean hasErrors()
     142    {
     143        for (Directory directory : _directoryList) {
     144            if (directory.hasErrors())
     145                return true;
     146        }
     147        return false;
    123148    }
    124149}
  • trunk/src/com/drew/metadata/MetadataException.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata;
    1822
    1923import com.drew.lang.CompoundException;
     24import com.drew.lang.annotations.Nullable;
    2025
    2126/**
     27 * Base class for all metadata specific exceptions.
    2228 *
     29 * @author Drew Noakes http://drewnoakes.com
    2330 */
    2431public class MetadataException extends CompoundException
    2532{
    26     public MetadataException(String msg)
     33    private static final long serialVersionUID = 8612756143363919682L;
     34
     35    public MetadataException(@Nullable String msg)
    2736    {
    2837        super(msg);
    2938    }
    3039
    31     public MetadataException(Throwable exception)
     40    public MetadataException(@Nullable Throwable exception)
    3241    {
    3342        super(exception);
    3443    }
    3544
    36     public MetadataException(String msg, Throwable innerException)
     45    public MetadataException(@Nullable String msg, @Nullable Throwable innerException)
    3746    {
    3847        super(msg, innerException);
  • trunk/src/com/drew/metadata/MetadataReader.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata;
    1822
     23import com.drew.lang.BufferReader;
     24import com.drew.lang.annotations.NotNull;
     25
    1926/**
     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.
    2032 *
     33 * @author Drew Noakes http://drewnoakes.com
    2134 */
    2235public interface MetadataReader
    2336{
    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);
    2744}
  • trunk/src/com/drew/metadata/Tag.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata;
    1822
    19 import java.io.Serializable;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2025
    2126/**
     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.
    2229 *
     30 * @author Drew Noakes http://drewnoakes.com
    2331 */
    24 public class Tag implements Serializable
     32public class Tag
    2533{
    2634    private final int _tagType;
     35    @NotNull
    2736    private final Directory _directory;
    2837
    29     public Tag(int tagType, Directory directory)
     38    public Tag(int tagType, @NotNull Directory directory)
    3039    {
    3140        _tagType = tagType;
     
    3544    /**
    3645     * Gets the tag type as an int
     46     *
    3747     * @return the tag type as an int
    3848     */
     
    4555     * Gets the tag type in hex notation as a String with padded leading
    4656     * zeroes if necessary (i.e. <code>0x100E</code>).
     57     *
    4758     * @return the tag type as a string in hexadecimal notation
    4859     */
     60    @NotNull
    4961    public String getTagTypeHex()
    5062    {
     
    5769     * Get a description of the tag's value, considering enumerated values
    5870     * and units.
     71     *
    5972     * @return a description of the tag's value
    6073     */
    61     public String getDescription() throws MetadataException
     74    @Nullable
     75    public String getDescription()
    6276    {
    6377        return _directory.getDescription(_tagType);
     
    6781     * Get the name of the tag, such as <code>Aperture</code>, or
    6882     * <code>InteropVersion</code>.
     83     *
    6984     * @return the tag's name
    7085     */
     86    @NotNull
    7187    public String getTagName()
    7288    {
     
    7793     * Get the name of the directory in which the tag exists, such as
    7894     * <code>Exif</code>, <code>GPS</code> or <code>Interoperability</code>.
     95     *
    7996     * @return name of the directory in which this tag exists
    8097     */
     98    @NotNull
    8199    public String getDirectoryName()
    82100    {
     
    85103
    86104    /**
    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     *
    89107     * @return the tag's type and value
    90108     */
     109    @NotNull
    91110    public String toString()
    92111    {
    93         String description;
    94         try {
    95             description = getDescription();
    96         } catch (MetadataException e) {
     112        String description = getDescription();
     113        if (description==null)
    97114            description = _directory.getString(getTagType()) + " (unable to formulate description)";
    98         }
    99115        return "[" + _directory.getName() + "] " + getTagName() + " - " + description;
    100116    }
  • trunk/src/com/drew/metadata/TagDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata;
    1622
    17 import java.io.Serializable;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25
     26import java.lang.reflect.Array;
    1827
    1928/**
    2029 * Abstract base class for all tag descriptor classes.  Implementations are responsible for
    21  * providing the human-readable string represenation of tag values stored in a directory.
     30 * providing the human-readable string representation of tag values stored in a directory.
    2231 * The directory is provided to the tag descriptor via its constructor.
     32 *
     33 * @author Drew Noakes http://drewnoakes.com
    2334 */
    24 public abstract class TagDescriptor implements Serializable
     35public abstract class TagDescriptor<T extends Directory>
    2536{
    26     protected final Directory _directory;
     37    @NotNull
     38    protected final T _directory;
    2739
    28     public TagDescriptor(Directory directory)
     40    public TagDescriptor(@NotNull T directory)
    2941    {
    3042        _directory = directory;
     
    3446     * Returns a descriptive value of the the specified tag for this image.
    3547     * 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     *
    4151     * @param tagType the tag to find a description for
    4252     * @return a description of the image's value for the specified tag, or
    4353     *         <code>null</code> if the tag hasn't been defined.
    4454     */
    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] -&gt; 2.10</li>
     83     *     <li>[0 1 0 0] -&gt; 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    }
    46107}
  • trunk/src/com/drew/metadata/exif/CanonMakernoteDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
     28 * Provides human-readable string representations of tag values stored in a <code>CanonMakernoteDirectory</code>.
    2429 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class CanonMakernoteDescriptor extends TagDescriptor
     32public class CanonMakernoteDescriptor extends TagDescriptor<CanonMakernoteDirectory>
    2733{
    28     public CanonMakernoteDescriptor(Directory directory)
     34    public CanonMakernoteDescriptor(@NotNull CanonMakernoteDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        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:
    3746                return getFlashActivityDescription();
    38             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE:
     47            case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE:
    3948                return getFocusTypeDescription();
    40             case CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM:
     49            case CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM:
    4150                return getDigitalZoomDescription();
    42             case CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY:
     51            case CanonMakernoteDirectory.CameraSettings.TAG_QUALITY:
    4352                return getQualityDescription();
    44             case CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE:
     53            case CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE:
    4554                return getMacroModeDescription();
    46             case CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY:
     55            case CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY:
    4756                return getSelfTimerDelayDescription();
    48             case CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE:
     57            case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE:
    4958                return getFlashModeDescription();
    50             case CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE:
     59            case CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE:
    5160                return getContinuousDriveModeDescription();
    52             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1:
     61            case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1:
    5362                return getFocusMode1Description();
    54             case CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE:
     63            case CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE:
    5564                return getImageSizeDescription();
    56             case CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE:
     65            case CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE:
    5766                return getEasyShootingModeDescription();
    58             case CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST:
     67            case CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST:
    5968                return getContrastDescription();
    60             case CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION:
     69            case CanonMakernoteDirectory.CameraSettings.TAG_SATURATION:
    6170                return getSaturationDescription();
    62             case CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS:
     71            case CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS:
    6372                return getSharpnessDescription();
    64             case CanonMakernoteDirectory.TAG_CANON_STATE1_ISO:
     73            case CanonMakernoteDirectory.CameraSettings.TAG_ISO:
    6574                return getIsoDescription();
    66             case CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE:
     75            case CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE:
    6776                return getMeteringModeDescription();
    68             case CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED:
     77            case CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED:
    6978                return getAfPointSelectedDescription();
    70             case CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE:
     79            case CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE:
    7180                return getExposureModeDescription();
    72             case CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH:
     81            case CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH:
    7382                return getLongFocalLengthDescription();
    74             case CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH:
     83            case CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH:
    7584                return getShortFocalLengthDescription();
    76             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM:
     85            case CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM:
    7786                return getFocalUnitsPerMillimetreDescription();
    78             case CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS:
     87            case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS:
    7988                return getFlashDetailsDescription();
    80             case CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2:
     89            case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2:
    8190                return getFocusMode2Description();
    82             case CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE:
     91            case CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE:
    8392                return getWhiteBalanceDescription();
    84             case CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED:
     93            case CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED:
    8594                return getAfPointUsedDescription();
    86             case CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS:
     95            case CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS:
    8796                return getFlashBiasDescription();
    88             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION:
    89                 return getLongExposureNoiseReductionDescription();
    90             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS:
    91                 return getShutterAutoExposureLockButtonDescription();
    92             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP:
    93                 return getMirrorLockupDescription();
    94             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL:
    95                 return getTvAndAvExposureLevelDescription();
    96             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT:
    97                 return getAutoFocusAssistLightDescription();
    98             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE:
    99                 return getShutterSpeedInAvModeDescription();
    100             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING:
    101                 return getAutoExposureBrackettingSequenceAndAutoCancellationDescription();
    102             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC:
    103                 return getShutterCurtainSyncDescription();
    104             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP:
    105                 return getLensAutoFocusStopButtonDescription();
    106             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION:
    107                 return getFillFlashReductionDescription();
    108             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN:
    109                 return getMenuButtonReturnPositionDescription();
    110             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION:
    111                 return getSetButtonFunctionWhenShootingDescription();
    112             case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING:
    113                 return getSensorCleaningDescription();
    114             default:
    115                 return _directory.getString(tagType);
    116         }
    117     }
    118 
    119     public String getLongExposureNoiseReductionDescription() throws MetadataException
    120     {
    121         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION)) return null;
    122         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
     97
     98            // It turns out that these values are dependent upon the camera model and therefore the below code was
     99            // incorrect for some Canon models.  This needs to be revisited.
     100
     101//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION:
     102//                return getLongExposureNoiseReductionDescription();
     103//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS:
     104//                return getShutterAutoExposureLockButtonDescription();
     105//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP:
     106//                return getMirrorLockupDescription();
     107//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL:
     108//                return getTvAndAvExposureLevelDescription();
     109//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT:
     110//                return getAutoFocusAssistLightDescription();
     111//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE:
     112//                return getShutterSpeedInAvModeDescription();
     113//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING:
     114//                return getAutoExposureBrackettingSequenceAndAutoCancellationDescription();
     115//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC:
     116//                return getShutterCurtainSyncDescription();
     117//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP:
     118//                return getLensAutoFocusStopButtonDescription();
     119//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION:
     120//                return getFillFlashReductionDescription();
     121//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN:
     122//                return getMenuButtonReturnPositionDescription();
     123//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION:
     124//                return getSetButtonFunctionWhenShootingDescription();
     125//            case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING:
     126//                return getSensorCleaningDescription();
     127            default:
     128                return super.getDescription(tagType);
     129        }
     130    }
     131
     132    @Nullable
     133    public String getSerialNumberDescription()
     134    {
     135        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_SERIAL_NUMBER);
     136        if (value==null)
     137            return null;
     138        return String.format("%04X%05d", (value >> 8) & 0xFF, value & 0xFF);
     139    }
     140
     141/*
     142    @Nullable
     143    public String getLongExposureNoiseReductionDescription()
     144    {
     145        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
     146        if (value==null)
     147            return null;
    123148        switch (value) {
    124149            case 0:     return "Off";
     
    127152        }
    128153    }
    129     public String getShutterAutoExposureLockButtonDescription() throws MetadataException
    130     {
    131         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS)) return null;
    132         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
     154
     155    @Nullable
     156    public String getShutterAutoExposureLockButtonDescription()
     157    {
     158        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
     159        if (value==null)
     160            return null;
    133161        switch (value) {
    134162            case 0:     return "AF/AE lock";
     
    139167        }
    140168    }
    141     public String getMirrorLockupDescription() throws MetadataException
    142     {
    143         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP)) return null;
    144         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
     169
     170    @Nullable
     171    public String getMirrorLockupDescription()
     172    {
     173        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
     174        if (value==null)
     175            return null;
    145176        switch (value) {
    146177            case 0:     return "Disabled";
     
    149180        }
    150181    }
    151     public String getTvAndAvExposureLevelDescription() throws MetadataException
    152     {
    153         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL)) return null;
    154         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
     182
     183    @Nullable
     184    public String getTvAndAvExposureLevelDescription()
     185    {
     186        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
     187        if (value==null)
     188            return null;
    155189        switch (value) {
    156190            case 0:     return "1/2 stop";
     
    159193        }
    160194    }
    161     public String getAutoFocusAssistLightDescription() throws MetadataException
    162     {
    163         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT)) return null;
    164         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
     195
     196    @Nullable
     197    public String getAutoFocusAssistLightDescription()
     198    {
     199        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
     200        if (value==null)
     201            return null;
    165202        switch (value) {
    166203            case 0:     return "On (Auto)";
     
    169206        }
    170207    }
    171     public String getShutterSpeedInAvModeDescription() throws MetadataException
    172     {
    173         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE)) return null;
    174         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
     208
     209    @Nullable
     210    public String getShutterSpeedInAvModeDescription()
     211    {
     212        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
     213        if (value==null)
     214            return null;
    175215        switch (value) {
    176216            case 0:     return "Automatic";
     
    179219        }
    180220    }
    181     public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() throws MetadataException
    182     {
    183         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING)) return null;
    184         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING);
     221
     222    @Nullable
     223    public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription()
     224    {
     225        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING);
     226        if (value==null)
     227            return null;
    185228        switch (value) {
    186229            case 0:     return "0,-,+ / Enabled";
     
    191234        }
    192235    }
    193     public String getShutterCurtainSyncDescription() throws MetadataException
    194     {
    195         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC)) return null;
    196         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
     236
     237    @Nullable
     238    public String getShutterCurtainSyncDescription()
     239    {
     240        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
     241        if (value==null)
     242            return null;
    197243        switch (value) {
    198244            case 0:     return "1st Curtain Sync";
     
    201247        }
    202248    }
    203     public String getLensAutoFocusStopButtonDescription() throws MetadataException
    204     {
    205         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP)) return null;
    206         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
     249
     250    @Nullable
     251    public String getLensAutoFocusStopButtonDescription()
     252    {
     253        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
     254        if (value==null)
     255            return null;
    207256        switch (value) {
    208257            case 0:     return "AF stop";
     
    212261        }
    213262    }
    214     public String getFillFlashReductionDescription() throws MetadataException
    215     {
    216         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION)) return null;
    217         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
     263
     264    @Nullable
     265    public String getFillFlashReductionDescription()
     266    {
     267        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
     268        if (value==null)
     269            return null;
    218270        switch (value) {
    219271            case 0:     return "Enabled";
     
    222274        }
    223275    }
    224     public String getMenuButtonReturnPositionDescription() throws MetadataException
    225     {
    226         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN)) return null;
    227         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
     276
     277    @Nullable
     278    public String getMenuButtonReturnPositionDescription()
     279    {
     280        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
     281        if (value==null)
     282            return null;
    228283        switch (value) {
    229284            case 0:     return "Top";
     
    233288        }
    234289    }
    235     public String getSetButtonFunctionWhenShootingDescription() throws MetadataException
    236     {
    237         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION)) return null;
    238         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
     290
     291    @Nullable
     292    public String getSetButtonFunctionWhenShootingDescription()
     293    {
     294        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
     295        if (value==null)
     296            return null;
    239297        switch (value) {
    240298            case 0:     return "Not Assigned";
     
    245303        }
    246304    }
    247     public String getSensorCleaningDescription() throws MetadataException
    248     {
    249         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING)) return null;
    250         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
     305
     306    @Nullable
     307    public String getSensorCleaningDescription()
     308    {
     309        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
     310        if (value==null)
     311            return null;
    251312        switch (value) {
    252313            case 0:     return "Disabled";
     
    255316        }
    256317    }
    257 
    258     public String getFlashBiasDescription() throws MetadataException
    259     {
    260         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS)) return null;
    261 
    262         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS);
     318*/
     319
     320    @Nullable
     321    public String getFlashBiasDescription()
     322    {
     323        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS);
     324
     325        if (value==null)
     326            return null;
    263327
    264328        boolean isNegative = false;
     
    278342    }
    279343
    280     public String getAfPointUsedDescription() throws MetadataException
    281     {
    282         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED)) return null;
    283         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED);
     344    @Nullable
     345    public String getAfPointUsedDescription()
     346    {
     347        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED);
     348        if (value==null)
     349            return null;
    284350        if ((value & 0x7) == 0) {
    285351            return "Right";
     
    293359    }
    294360
    295     public String getWhiteBalanceDescription() throws MetadataException
    296     {
    297         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE)) return null;
    298         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE);
     361    @Nullable
     362    public String getWhiteBalanceDescription()
     363    {
     364        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE);
     365        if (value==null)
     366            return null;
    299367        switch (value) {
    300368            case 0:
     
    307375                return "Tungsten";
    308376            case 4:
    309                 return "Flourescent";
     377                return "Florescent";
    310378            case 5:
    311379                return "Flash";
     
    317385    }
    318386
    319     public String getFocusMode2Description() throws MetadataException
    320     {
    321         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2)) return null;
    322         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2);
     387    @Nullable
     388    public String getFocusMode2Description()
     389    {
     390        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2);
     391        if (value==null)
     392            return null;
    323393        switch (value) {
    324394            case 0:
     
    331401    }
    332402
    333     public String getFlashDetailsDescription() throws MetadataException
    334     {
    335         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS)) return null;
    336         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS);
    337         if (((value << 14) & 1) > 0) {
     403    @Nullable
     404    public String getFlashDetailsDescription()
     405    {
     406        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS);
     407        if (value==null)
     408            return null;
     409        if (((value >> 14) & 1) > 0) {
    338410            return "External E-TTL";
    339411        }
    340         if (((value << 13) & 1) > 0) {
     412        if (((value >> 13) & 1) > 0) {
    341413            return "Internal flash";
    342414        }
    343         if (((value << 11) & 1) > 0) {
     415        if (((value >> 11) & 1) > 0) {
    344416            return "FP sync used";
    345417        }
    346         if (((value << 4) & 1) > 0) {
     418        if (((value >> 4) & 1) > 0) {
    347419            return "FP sync enabled";
    348420        }
     
    350422    }
    351423
    352     public String getFocalUnitsPerMillimetreDescription() throws MetadataException
    353     {
    354         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM)) return "";
    355         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM);
     424    @Nullable
     425    public String getFocalUnitsPerMillimetreDescription()
     426    {
     427        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM);
     428        if (value==null)
     429            return null;
    356430        if (value != 0) {
    357431            return Integer.toString(value);
     
    361435    }
    362436
    363     public String getShortFocalLengthDescription() throws MetadataException
    364     {
    365         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH)) return null;
    366         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH);
     437    @Nullable
     438    public String getShortFocalLengthDescription()
     439    {
     440        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH);
     441        if (value==null)
     442            return null;
    367443        String units = getFocalUnitsPerMillimetreDescription();
    368444        return Integer.toString(value) + " " + units;
    369445    }
    370446
    371     public String getLongFocalLengthDescription() throws MetadataException
    372     {
    373         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH)) return null;
    374         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH);
     447    @Nullable
     448    public String getLongFocalLengthDescription()
     449    {
     450        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH);
     451        if (value==null)
     452            return null;
    375453        String units = getFocalUnitsPerMillimetreDescription();
    376454        return Integer.toString(value) + " " + units;
    377455    }
    378456
    379     public String getExposureModeDescription() throws MetadataException
    380     {
    381         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE)) return null;
    382         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE);
     457    @Nullable
     458    public String getExposureModeDescription()
     459    {
     460        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE);
     461        if (value==null)
     462            return null;
    383463        switch (value) {
    384464            case 0:
     
    399479    }
    400480
    401     public String getAfPointSelectedDescription() throws MetadataException
    402     {
    403         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED)) return null;
    404         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED);
     481    @Nullable
     482    public String getAfPointSelectedDescription()
     483    {
     484        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED);
     485        if (value==null)
     486            return null;
    405487        switch (value) {
    406488            case 0x3000:
     
    419501    }
    420502
    421     public String getMeteringModeDescription() throws MetadataException
    422     {
    423         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE)) return null;
    424         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE);
     503    @Nullable
     504    public String getMeteringModeDescription()
     505    {
     506        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE);
     507        if (value==null)
     508            return null;
    425509        switch (value) {
    426510            case 3:
     
    435519    }
    436520
    437     public String getIsoDescription() throws MetadataException
    438     {
    439         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO)) return null;
    440         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO);
     521    @Nullable
     522    public String getIsoDescription()
     523    {
     524        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_ISO);
     525        if (value==null)
     526            return null;
     527
     528        // Canon PowerShot S3 is special
     529        int canonMask = 0x4000;
     530        if ((value & canonMask) > 0)
     531            return "" + (value & ~canonMask);
     532
    441533        switch (value) {
    442534            case 0:
     
    457549    }
    458550
    459     public String getSharpnessDescription() throws MetadataException
    460     {
    461         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS)) return null;
    462         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS);
     551    @Nullable
     552    public String getSharpnessDescription()
     553    {
     554        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS);
     555        if (value==null)
     556            return null;
    463557        switch (value) {
    464558            case 0xFFFF:
     
    473567    }
    474568
    475     public String getSaturationDescription() throws MetadataException
    476     {
    477         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION)) return null;
    478         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION);
     569    @Nullable
     570    public String getSaturationDescription()
     571    {
     572        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SATURATION);
     573        if (value==null)
     574            return null;
    479575        switch (value) {
    480576            case 0xFFFF:
     
    489585    }
    490586
    491     public String getContrastDescription() throws MetadataException
    492     {
    493         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST)) return null;
    494         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST);
     587    @Nullable
     588    public String getContrastDescription()
     589    {
     590        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST);
     591        if (value==null)
     592            return null;
    495593        switch (value) {
    496594            case 0xFFFF:
     
    505603    }
    506604
    507     public String getEasyShootingModeDescription() throws MetadataException
    508     {
    509         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE)) return null;
    510         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE);
     605    @Nullable
     606    public String getEasyShootingModeDescription()
     607    {
     608        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE);
     609        if (value==null)
     610            return null;
    511611        switch (value) {
    512612            case 0:
     
    539639    }
    540640
    541     public String getImageSizeDescription() throws MetadataException
    542     {
    543         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE)) return null;
    544         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE);
     641    @Nullable
     642    public String getImageSizeDescription()
     643    {
     644        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE);
     645        if (value==null)
     646            return null;
    545647        switch (value) {
    546648            case 0:
     
    555657    }
    556658
    557     public String getFocusMode1Description() throws MetadataException
    558     {
    559         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1)) return null;
    560         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1);
     659    @Nullable
     660    public String getFocusMode1Description()
     661    {
     662        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1);
     663        if (value==null)
     664            return null;
    561665        switch (value) {
    562666            case 0:
     
    580684    }
    581685
    582     public String getContinuousDriveModeDescription() throws MetadataException
    583     {
    584         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE)) return null;
    585         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE);
    586         switch (value) {
    587             case 0:
    588                 if (_directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY) == 0) {
    589                     return "Single shot";
    590                 } else {
    591                     return "Single shot with self-timer";
    592                 }
     686    @Nullable
     687    public String getContinuousDriveModeDescription()
     688    {
     689        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE);
     690        if (value==null)
     691            return null;
     692        switch (value) {
     693            case 0:
     694                final Integer delay = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY);
     695                if (delay!=null)
     696                    return delay == 0 ? "Single shot" : "Single shot with self-timer";
    593697            case 1:
    594698                return "Continuous";
    595             default:
    596                 return "Unknown (" + value + ")";
    597         }
    598     }
    599 
    600     public String getFlashModeDescription() throws MetadataException
    601     {
    602         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE)) return null;
    603         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE);
     699        }
     700        return "Unknown (" + value + ")";
     701    }
     702
     703    @Nullable
     704    public String getFlashModeDescription()
     705    {
     706        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE);
     707        if (value==null)
     708            return null;
    604709        switch (value) {
    605710            case 0:
     
    619724            case 16:
    620725                // note: this value not set on Canon D30
    621                 return "Extenal flash";
    622             default:
    623                 return "Unknown (" + value + ")";
    624         }
    625     }
    626 
    627     public String getSelfTimerDelayDescription() throws MetadataException
    628     {
    629         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY)) return null;
    630         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY);
     726                return "External flash";
     727            default:
     728                return "Unknown (" + value + ")";
     729        }
     730    }
     731
     732    @Nullable
     733    public String getSelfTimerDelayDescription()
     734    {
     735        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY);
     736        if (value==null)
     737            return null;
    631738        if (value == 0) {
    632739            return "Self timer not used";
     
    637744    }
    638745
    639     public String getMacroModeDescription() throws MetadataException
    640     {
    641         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE)) return null;
    642         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE);
     746    @Nullable
     747    public String getMacroModeDescription()
     748    {
     749        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE);
     750        if (value==null)
     751            return null;
    643752        switch (value) {
    644753            case 1:
     
    651760    }
    652761
    653     public String getQualityDescription() throws MetadataException
    654     {
    655         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY)) return null;
    656         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY);
     762    @Nullable
     763    public String getQualityDescription()
     764    {
     765        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_QUALITY);
     766        if (value==null)
     767            return null;
    657768        switch (value) {
    658769            case 2:
     
    667778    }
    668779
    669     public String getDigitalZoomDescription() throws MetadataException
    670     {
    671         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM)) return null;
    672         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM);
     780    @Nullable
     781    public String getDigitalZoomDescription()
     782    {
     783        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM);
     784        if (value==null)
     785            return null;
    673786        switch (value) {
    674787            case 0:
     
    683796    }
    684797
    685     public String getFocusTypeDescription() throws MetadataException
    686     {
    687         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE)) return null;
    688         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE);
     798    @Nullable
     799    public String getFocusTypeDescription()
     800    {
     801        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE);
     802        if (value==null)
     803            return null;
    689804        switch (value) {
    690805            case 0:
     
    701816    }
    702817
    703     public String getFlashActivityDescription() throws MetadataException
    704     {
    705         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY)) return null;
    706         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY);
     818    @Nullable
     819    public String getFlashActivityDescription()
     820    {
     821        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_ACTIVITY);
     822        if (value==null)
     823            return null;
    707824        switch (value) {
    708825            case 0:
  • trunk/src/com/drew/metadata/exif/CanonMakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2732 *
    2833 * Many tag definitions explained here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
     34 *
     35 * @author Drew Noakes http://drewnoakes.com
    2936 */
    3037public class CanonMakernoteDirectory extends Directory
    3138{
    32     // CANON cameras have some funny bespoke fields that need further processing...
    33     public static final int TAG_CANON_CAMERA_STATE_1 = 0x0001;
    34     public static final int TAG_CANON_CAMERA_STATE_2 = 0x0004;
    35 
    36     public static final int TAG_CANON_IMAGE_TYPE = 0x0006;
    37     public static final int TAG_CANON_FIRMWARE_VERSION = 0x0007;
    38     public static final int TAG_CANON_IMAGE_NUMBER = 0x0008;
    39     public static final int TAG_CANON_OWNER_NAME = 0x0009;
    40     /**
    41      * To display serial number as on camera use: printf( "%04X%05d", highbyte, lowbyte )
    42      * TODO handle this in CanonMakernoteDescriptor
    43      */
    44     public static final int TAG_CANON_SERIAL_NUMBER = 0x000C;
    45     public static final int TAG_CANON_UNKNOWN_1 = 0x000D;
    46     public static final int TAG_CANON_CUSTOM_FUNCTIONS = 0x000F;
    47 
    48     // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    49     /**
    50      * 1 = Macro
    51      * 2 = Normal
    52      */
    53     public static final int TAG_CANON_STATE1_MACRO_MODE = 0xC101;
    54     public static final int TAG_CANON_STATE1_SELF_TIMER_DELAY = 0xC102;
    55     /**
    56      * 2 = Normal
    57      * 3 = Fine
    58      * 5 = Superfine
    59      */
    60     public static final int TAG_CANON_STATE1_QUALITY = 0xC103;
    61     /**
    62      * 0 = Flash Not Fired
    63      * 1 = Auto
    64      * 2 = On
    65      * 3 = Red Eye Reduction
    66      * 4 = Slow Synchro
    67      * 5 = Auto + Red Eye Reduction
    68      * 6 = On + Red Eye Reduction
    69      * 16 = External Flash
    70      */
    71     public static final int TAG_CANON_STATE1_FLASH_MODE = 0xC104;
    72     /**
    73      * 0 = Single Frame or Timer Mode
    74      * 1 = Continuous
    75      */
    76     public static final int TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE = 0xC105;
    77     public static final int TAG_CANON_STATE1_UNKNOWN_2 = 0xC106;
    78     /**
    79      * 0 = One-Shot
    80      * 1 = AI Servo
    81      * 2 = AI Focus
    82      * 3 = Manual Focus
    83      * 4 = Single
    84      * 5 = Continuous
    85      * 6 = Manual Focus
    86      */
    87     public static final int TAG_CANON_STATE1_FOCUS_MODE_1 = 0xC107;
    88     public static final int TAG_CANON_STATE1_UNKNOWN_3 = 0xC108;
    89     public static final int TAG_CANON_STATE1_UNKNOWN_4 = 0xC109;
    90     /**
    91      * 0 = Large
    92      * 1 = Medium
    93      * 2 = Small
    94      */
    95     public static final int TAG_CANON_STATE1_IMAGE_SIZE = 0xC10A;
    96     /**
    97      * 0 = Full Auto
    98      * 1 = Manual
    99      * 2 = Landscape
    100      * 3 = Fast Shutter
    101      * 4 = Slow Shutter
    102      * 5 = Night
    103      * 6 = Black & White
    104      * 7 = Sepia
    105      * 8 = Portrait
    106      * 9 = Sports
    107      * 10 = Macro / Close-Up
    108      * 11 = Pan Focus
    109      */
    110     public static final int TAG_CANON_STATE1_EASY_SHOOTING_MODE = 0xC10B;
    111     /**
    112      * 0 = No Digital Zoom
    113      * 1 = 2x
    114      * 2 = 4x
    115      */
    116     public static final int TAG_CANON_STATE1_DIGITAL_ZOOM = 0xC10C;
    117     /**
    118      * 0 = Normal
    119      * 1 = High
    120      * 65535 = Low
    121      */
    122     public static final int TAG_CANON_STATE1_CONTRAST = 0xC10D;
    123     /**
    124      * 0 = Normal
    125      * 1 = High
    126      * 65535 = Low
    127      */
    128     public static final int TAG_CANON_STATE1_SATURATION = 0xC10E;
    129     /**
    130      * 0 = Normal
    131      * 1 = High
    132      * 65535 = Low
    133      */
    134     public static final int TAG_CANON_STATE1_SHARPNESS = 0xC10F;
    135     /**
    136      * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
    137      * 15 = Auto ISO
    138      * 16 = ISO 50
    139      * 17 = ISO 100
    140      * 18 = ISO 200
    141      * 19 = ISO 400
    142      */
    143     public static final int TAG_CANON_STATE1_ISO = 0xC110;
    144     /**
    145      * 3 = Evaluative
    146      * 4 = Partial
    147      * 5 = Centre Weighted
    148      */
    149     public static final int TAG_CANON_STATE1_METERING_MODE = 0xC111;
    150     /**
    151      * 0 = Manual
    152      * 1 = Auto
    153      * 3 = Close-up (Macro)
    154      * 8 = Locked (Pan Mode)
    155      */
    156     public static final int TAG_CANON_STATE1_FOCUS_TYPE = 0xC112;
    157     /**
    158      * 12288 = None (Manual Focus)
    159      * 12289 = Auto Selected
    160      * 12290 = Right
    161      * 12291 = Centre
    162      * 12292 = Left
    163      */
    164     public static final int TAG_CANON_STATE1_AF_POINT_SELECTED = 0xC113;
    165     /**
    166      * 0 = Easy Shooting (See Easy Shooting Mode)
    167      * 1 = Program
    168      * 2 = Tv-Priority
    169      * 3 = Av-Priority
    170      * 4 = Manual
    171      * 5 = A-DEP
    172      */
    173     public static final int TAG_CANON_STATE1_EXPOSURE_MODE = 0xC114;
    174     public static final int TAG_CANON_STATE1_UNKNOWN_7 = 0xC115;
    175     public static final int TAG_CANON_STATE1_UNKNOWN_8 = 0xC116;
    176     public static final int TAG_CANON_STATE1_LONG_FOCAL_LENGTH = 0xC117;
    177     public static final int TAG_CANON_STATE1_SHORT_FOCAL_LENGTH = 0xC118;
    178     public static final int TAG_CANON_STATE1_FOCAL_UNITS_PER_MM = 0xC119;
    179     public static final int TAG_CANON_STATE1_UNKNOWN_9 = 0xC11A;
    180     public static final int TAG_CANON_STATE1_UNKNOWN_10 = 0xC11B;
    181     /**
    182      * 0 = Flash Did Not Fire
    183      * 1 = Flash Fired
    184      */
    185     public static final int TAG_CANON_STATE1_FLASH_ACTIVITY = 0xC11C;
    186     public static final int TAG_CANON_STATE1_FLASH_DETAILS = 0xC11D;
    187     public static final int TAG_CANON_STATE1_UNKNOWN_12 = 0xC11E;
    188     public static final int TAG_CANON_STATE1_UNKNOWN_13 = 0xC11F;
    189     /**
    190      * 0 = Focus Mode: Single
    191      * 1 = Focus Mode: Continuous
    192      */
    193     public static final int TAG_CANON_STATE1_FOCUS_MODE_2 = 0xC120;
    194 
    195     /**
    196      * 0 = Auto
    197      * 1 = Sunny
    198      * 2 = Cloudy
    199      * 3 = Tungsten
    200      * 4 = Flourescent
    201      * 5 = Flash
    202      * 6 = Custom
    203      */
    204     public static final int TAG_CANON_STATE2_WHITE_BALANCE = 0xC207;
    205     public static final int TAG_CANON_STATE2_SEQUENCE_NUMBER = 0xC209;
    206     public static final int TAG_CANON_STATE2_AF_POINT_USED = 0xC20E;
    207     /**
    208      * The value of this tag may be translated into a flash bias value, in EV.
    209      *
    210      * 0xffc0 = -2 EV
    211      * 0xffcc = -1.67 EV
    212      * 0xffd0 = -1.5 EV
    213      * 0xffd4 = -1.33 EV
    214      * 0xffe0 = -1 EV
    215      * 0xffec = -0.67 EV
    216      * 0xfff0 = -0.5 EV
    217      * 0xfff4 = -0.33 EV
    218      * 0x0000 = 0 EV
    219      * 0x000c = 0.33 EV
    220      * 0x0010 = 0.5 EV
    221      * 0x0014 = 0.67 EV
    222      * 0x0020 = 1 EV
    223      * 0x002c = 1.33 EV
    224      * 0x0030 = 1.5 EV
    225      * 0x0034 = 1.67 EV
    226      * 0x0040 = 2 EV
    227      */
    228     public static final int TAG_CANON_STATE2_FLASH_BIAS = 0xC20F;
    229     public static final int TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING = 0xC210;
    230     public static final int TAG_CANON_STATE2_AEB_BRACKET_VALUE = 0xC211;
    231     public static final int TAG_CANON_STATE2_SUBJECT_DISTANCE = 0xC213;
    232 
    233     /**
    234      * Long Exposure Noise Reduction
    235      * 0 = Off
    236      * 1 = On
    237      */
    238     public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
    239 
    240     /**
    241      * Shutter/Auto Exposure-lock buttons
    242      * 0 = AF/AE lock
    243      * 1 = AE lock/AF
    244      * 2 = AF/AF lock
    245      * 3 = AE+release/AE+AF
    246      */
    247     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
    248 
    249     /**
    250      * Mirror lockup
    251      * 0 = Disable
    252      * 1 = Enable
    253      */
    254     public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
    255 
    256     /**
    257      * Tv/Av and exposure level
    258      * 0 = 1/2 stop
    259      * 1 = 1/3 stop
    260      */
    261     public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
    262 
    263     /**
    264      * AF-assist light
    265      * 0 = On (Auto)
    266      * 1 = Off
    267      */
    268     public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
    269 
    270     /**
    271      * Shutter speed in Av mode
    272      * 0 = Automatic
    273      * 1 = 1/200 (fixed)
    274      */
    275     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
    276 
    277     /**
    278      * Auto-Exposure Bracketting sequence/auto cancellation
    279      * 0 = 0,-,+ / Enabled
    280      * 1 = 0,-,+ / Disabled
    281      * 2 = -,0,+ / Enabled
    282      * 3 = -,0,+ / Disabled
    283      */
    284     public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307;
    285 
    286     /**
    287      * Shutter Curtain Sync
    288      * 0 = 1st Curtain Sync
    289      * 1 = 2nd Curtain Sync
    290      */
    291     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
    292 
    293     /**
    294      * Lens Auto-Focus stop button Function Switch
    295      * 0 = AF stop
    296      * 1 = Operate AF
    297      * 2 = Lock AE and start timer
    298      */
    299     public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
    300 
    301     /**
    302      * Auto reduction of fill flash
    303      * 0 = Enable
    304      * 1 = Disable
    305      */
    306     public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
    307 
    308     /**
    309      * Menu button return position
    310      * 0 = Top
    311      * 1 = Previous (volatile)
    312      * 2 = Previous
    313      */
    314     public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
    315 
    316     /**
    317      * SET button function when shooting
    318      * 0 = Not Assigned
    319      * 1 = Change Quality
    320      * 2 = Change ISO Speed
    321      * 3 = Select Parameters
    322      */
    323     public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;
    324 
    325     /**
    326      * Sensor cleaning
    327      * 0 = Disable
    328      * 1 = Enable
    329      */
    330     public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;
     39    // These TAG_*_ARRAY Exif tags map to arrays of int16 values which are split out into separate 'fake' tags.
     40    // When an attempt is made to set one of these on the directory, it is split and the corresponding offset added to the tagType.
     41    // So the resulting tag is the offset + the index into the array.
     42
     43    private static final int TAG_CAMERA_SETTINGS_ARRAY  = 0x0001;
     44    private static final int TAG_FOCAL_LENGTH_ARRAY = 0x0002;
     45    private static final int TAG_SHOT_INFO_ARRAY = 0x0004;
     46    private static final int TAG_PANORAMA_ARRAY = 0x0005;
     47
     48    public static final int TAG_CANON_IMAGE_TYPE            = 0x0006;
     49    public static final int TAG_CANON_FIRMWARE_VERSION      = 0x0007;
     50    public static final int TAG_CANON_IMAGE_NUMBER          = 0x0008;
     51    public static final int TAG_CANON_OWNER_NAME            = 0x0009;
     52    public static final int TAG_CANON_SERIAL_NUMBER         = 0x000C;
     53    public static final int TAG_CAMERA_INFO_ARRAY           = 0x000D; // depends upon model, so leave for now
     54    public static final int TAG_CANON_FILE_LENGTH           = 0x000E;
     55    public static final int TAG_CANON_CUSTOM_FUNCTIONS_ARRAY = 0x000F; // depends upon model, so leave for now
     56    public static final int TAG_MODEL_ID                    = 0x0010;
     57    public static final int TAG_MOVIE_INFO_ARRAY            = 0x0011; // not currently decoded as not sure we see it in still images
     58    private static final int TAG_AF_INFO_ARRAY              = 0x0012; // not currently decoded
     59    public static final int TAG_THUMBNAIL_IMAGE_VALID_AREA  = 0x0013;
     60    public static final int TAG_SERIAL_NUMBER_FORMAT        = 0x0015;
     61    public static final int TAG_SUPER_MACRO                 = 0x001A;
     62    public static final int TAG_DATE_STAMP_MODE             = 0x001C;
     63    public static final int TAG_MY_COLORS                   = 0x001D;
     64    public static final int TAG_FIRMWARE_REVISION           = 0x001E;
     65    public static final int TAG_CATEGORIES                  = 0x0023;
     66    public static final int TAG_FACE_DETECT_ARRAY_1         = 0x0024;
     67    public static final int TAG_FACE_DETECT_ARRAY_2         = 0x0025;
     68    public static final int TAG_AF_INFO_ARRAY_2             = 0x0026;
     69    public static final int TAG_IMAGE_UNIQUE_ID             = 0x0028;
     70    public static final int TAG_RAW_DATA_OFFSET             = 0x0081;
     71    public static final int TAG_ORIGINAL_DECISION_DATA_OFFSET = 0x0083;
     72    public static final int TAG_CUSTOM_FUNCTIONS_1D_ARRAY   = 0x0090; // not currently decoded
     73    public static final int TAG_PERSONAL_FUNCTIONS_ARRAY    = 0x0091; // not currently decoded
     74    public static final int TAG_PERSONAL_FUNCTION_VALUES_ARRAY = 0x0092; // not currently decoded
     75    public static final int TAG_FILE_INFO_ARRAY             = 0x0093; // not currently decoded
     76    public static final int TAG_AF_POINTS_IN_FOCUS_1D       = 0x0094;
     77    public static final int TAG_LENS_MODEL                  = 0x0095;
     78    public static final int TAG_SERIAL_INFO_ARRAY           = 0x0096; // not currently decoded
     79    public static final int TAG_DUST_REMOVAL_DATA           = 0x0097;
     80    public static final int TAG_CROP_INFO                   = 0x0098; // not currently decoded
     81    public static final int TAG_CUSTOM_FUNCTIONS_ARRAY_2    = 0x0099; // not currently decoded
     82    public static final int TAG_ASPECT_INFO_ARRAY           = 0x009A; // not currently decoded
     83    public static final int TAG_PROCESSING_INFO_ARRAY       = 0x00A0; // not currently decoded
     84    public static final int TAG_TONE_CURVE_TABLE            = 0x00A1;
     85    public static final int TAG_SHARPNESS_TABLE             = 0x00A2;
     86    public static final int TAG_SHARPNESS_FREQ_TABLE        = 0x00A3;
     87    public static final int TAG_WHITE_BALANCE_TABLE         = 0x00A4;
     88    public static final int TAG_COLOR_BALANCE_ARRAY         = 0x00A9; // not currently decoded
     89    public static final int TAG_MEASURED_COLOR_ARRAY        = 0x00AA; // not currently decoded
     90    public static final int TAG_COLOR_TEMPERATURE           = 0x00AE;
     91    public static final int TAG_CANON_FLAGS_ARRAY           = 0x00B0; // not currently decoded
     92    public static final int TAG_MODIFIED_INFO_ARRAY         = 0x00B1; // not currently decoded
     93    public static final int TAG_TONE_CURVE_MATCHING         = 0x00B2;
     94    public static final int TAG_WHITE_BALANCE_MATCHING      = 0x00B3;
     95    public static final int TAG_COLOR_SPACE                 = 0x00B4;
     96    public static final int TAG_PREVIEW_IMAGE_INFO_ARRAY    = 0x00B6; // not currently decoded
     97    public static final int TAG_VRD_OFFSET                  = 0x00D0;
     98    public static final int TAG_SENSOR_INFO_ARRAY           = 0x00E0; // not currently decoded
     99    public static final int TAG_COLOR_DATA_ARRAY_2 = 0x4001; // depends upon camera model, not currently decoded
     100    public static final int TAG_COLOR_INFO_ARRAY_2          = 0x4003; // not currently decoded
     101    public static final int TAG_CUSTOM_PICTURE_STYLE_FILE_NAME = 0x4010;
     102    public static final int TAG_COLOR_INFO_ARRAY            = 0x4013; // not currently decoded
     103    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_1 = 0x4015; // not currently decoded
     104    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_2 = 0x4016; // not currently decoded
     105    public static final int TAG_LIGHTING_OPTIMIZER_ARRAY = 0x4018; // not currently decoded
     106    public static final int TAG_LENS_INFO_ARRAY             = 0x4019; // not currently decoded
     107    public static final int TAG_AMBIANCE_INFO_ARRAY         = 0x4020; // not currently decoded
     108    public static final int TAG_FILTER_INFO_ARRAY           = 0x4024; // not currently decoded
     109
     110    public final static class CameraSettings
     111    {
     112        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     113        private static final int OFFSET = 0xC100;
     114
     115        /**
     116         * 1 = Macro
     117         * 2 = Normal
     118         */
     119        public static final int TAG_MACRO_MODE = OFFSET + 0x01;
     120        public static final int TAG_SELF_TIMER_DELAY = OFFSET + 0x02;
     121        /**
     122         * 2 = Normal
     123         * 3 = Fine
     124         * 5 = Superfine
     125         */
     126        public static final int TAG_QUALITY = OFFSET + 0x03;
     127        /**
     128         * 0 = Flash Not Fired
     129         * 1 = Auto
     130         * 2 = On
     131         * 3 = Red Eye Reduction
     132         * 4 = Slow Synchro
     133         * 5 = Auto + Red Eye Reduction
     134         * 6 = On + Red Eye Reduction
     135         * 16 = External Flash
     136         */
     137        public static final int TAG_FLASH_MODE = OFFSET + 0x04;
     138        /**
     139         * 0 = Single Frame or Timer Mode
     140         * 1 = Continuous
     141         */
     142        public static final int TAG_CONTINUOUS_DRIVE_MODE = OFFSET + 0x05;
     143        public static final int TAG_UNKNOWN_2 = OFFSET + 0x06;
     144        /**
     145         * 0 = One-Shot
     146         * 1 = AI Servo
     147         * 2 = AI Focus
     148         * 3 = Manual Focus
     149         * 4 = Single
     150         * 5 = Continuous
     151         * 6 = Manual Focus
     152         */
     153        public static final int TAG_FOCUS_MODE_1 = OFFSET + 0x07;
     154        public static final int TAG_UNKNOWN_3 = OFFSET + 0x08;
     155        public static final int TAG_UNKNOWN_4 = OFFSET + 0x09;
     156        /**
     157         * 0 = Large
     158         * 1 = Medium
     159         * 2 = Small
     160         */
     161        public static final int TAG_IMAGE_SIZE = OFFSET + 0x0A;
     162        /**
     163         * 0 = Full Auto
     164         * 1 = Manual
     165         * 2 = Landscape
     166         * 3 = Fast Shutter
     167         * 4 = Slow Shutter
     168         * 5 = Night
     169         * 6 = Black & White
     170         * 7 = Sepia
     171         * 8 = Portrait
     172         * 9 = Sports
     173         * 10 = Macro / Close-Up
     174         * 11 = Pan Focus
     175         */
     176        public static final int TAG_EASY_SHOOTING_MODE = OFFSET + 0x0B;
     177        /**
     178         * 0 = No Digital Zoom
     179         * 1 = 2x
     180         * 2 = 4x
     181         */
     182        public static final int TAG_DIGITAL_ZOOM = OFFSET + 0x0C;
     183        /**
     184         * 0 = Normal
     185         * 1 = High
     186         * 65535 = Low
     187         */
     188        public static final int TAG_CONTRAST = OFFSET + 0x0D;
     189        /**
     190         * 0 = Normal
     191         * 1 = High
     192         * 65535 = Low
     193         */
     194        public static final int TAG_SATURATION = OFFSET + 0x0E;
     195        /**
     196         * 0 = Normal
     197         * 1 = High
     198         * 65535 = Low
     199         */
     200        public static final int TAG_SHARPNESS = OFFSET + 0x0F;
     201        /**
     202         * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
     203         * 15 = Auto ISO
     204         * 16 = ISO 50
     205         * 17 = ISO 100
     206         * 18 = ISO 200
     207         * 19 = ISO 400
     208         */
     209        public static final int TAG_ISO = OFFSET + 0x10;
     210        /**
     211         * 3 = Evaluative
     212         * 4 = Partial
     213         * 5 = Centre Weighted
     214         */
     215        public static final int TAG_METERING_MODE = OFFSET + 0x11;
     216        /**
     217         * 0 = Manual
     218         * 1 = Auto
     219         * 3 = Close-up (Macro)
     220         * 8 = Locked (Pan Mode)
     221         */
     222        public static final int TAG_FOCUS_TYPE = OFFSET + 0x12;
     223        /**
     224         * 12288 = None (Manual Focus)
     225         * 12289 = Auto Selected
     226         * 12290 = Right
     227         * 12291 = Centre
     228         * 12292 = Left
     229         */
     230        public static final int TAG_AF_POINT_SELECTED = OFFSET + 0x13;
     231        /**
     232         * 0 = Easy Shooting (See Easy Shooting Mode)
     233         * 1 = Program
     234         * 2 = Tv-Priority
     235         * 3 = Av-Priority
     236         * 4 = Manual
     237         * 5 = A-DEP
     238         */
     239        public static final int TAG_EXPOSURE_MODE = OFFSET + 0x14;
     240        public static final int TAG_UNKNOWN_7 = OFFSET + 0x15;
     241        public static final int TAG_UNKNOWN_8 = OFFSET + 0x16;
     242        public static final int TAG_LONG_FOCAL_LENGTH = OFFSET + 0x17;
     243        public static final int TAG_SHORT_FOCAL_LENGTH = OFFSET + 0x18;
     244        public static final int TAG_FOCAL_UNITS_PER_MM = OFFSET + 0x19;
     245        public static final int TAG_UNKNOWN_9 = OFFSET + 0x1A;
     246        public static final int TAG_UNKNOWN_10 = OFFSET + 0x1B;
     247        /**
     248         * 0 = Flash Did Not Fire
     249         * 1 = Flash Fired
     250         */
     251        public static final int TAG_FLASH_ACTIVITY = OFFSET + 0x1C;
     252        public static final int TAG_FLASH_DETAILS = OFFSET + 0x1D;
     253        public static final int TAG_UNKNOWN_12 = OFFSET + 0x1E;
     254        public static final int TAG_UNKNOWN_13 = OFFSET + 0x1F;
     255        /**
     256         * 0 = Focus Mode: Single
     257         * 1 = Focus Mode: Continuous
     258         */
     259        public static final int TAG_FOCUS_MODE_2 = OFFSET + 0x20;
     260    }
     261   
     262    public final static class FocalLength
     263    {
     264        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     265
     266        private static final int OFFSET = 0xC200;
     267       
     268        /**
     269         * 0 = Auto
     270         * 1 = Sunny
     271         * 2 = Cloudy
     272         * 3 = Tungsten
     273         * 4 = Florescent
     274         * 5 = Flash
     275         * 6 = Custom
     276         */
     277        public static final int TAG_WHITE_BALANCE = OFFSET + 0x07;
     278        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 0x09;
     279        public static final int TAG_AF_POINT_USED = OFFSET + 0x0E;
     280        /**
     281         * The value of this tag may be translated into a flash bias value, in EV.
     282         *
     283         * 0xffc0 = -2 EV
     284         * 0xffcc = -1.67 EV
     285         * 0xffd0 = -1.5 EV
     286         * 0xffd4 = -1.33 EV
     287         * 0xffe0 = -1 EV
     288         * 0xffec = -0.67 EV
     289         * 0xfff0 = -0.5 EV
     290         * 0xfff4 = -0.33 EV
     291         * 0x0000 = 0 EV
     292         * 0x000c = 0.33 EV
     293         * 0x0010 = 0.5 EV
     294         * 0x0014 = 0.67 EV
     295         * 0x0020 = 1 EV
     296         * 0x002c = 1.33 EV
     297         * 0x0030 = 1.5 EV
     298         * 0x0034 = 1.67 EV
     299         * 0x0040 = 2 EV
     300         */
     301        public static final int TAG_FLASH_BIAS = OFFSET + 0x0F;
     302        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 0x10;
     303        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 0x11;
     304        public static final int TAG_SUBJECT_DISTANCE = OFFSET + 0x13;
     305    }
     306   
     307    public final static class ShotInfo
     308    {
     309        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     310
     311        private static final int OFFSET = 0xC400;
     312
     313        public static final int TAG_AUTO_ISO = OFFSET + 1;
     314        public static final int TAG_BASE_ISO = OFFSET + 2;
     315        public static final int TAG_MEASURED_EV = OFFSET + 3;
     316        public static final int TAG_TARGET_APERTURE = OFFSET + 4;
     317        public static final int TAG_TARGET_EXPOSURE_TIME = OFFSET + 5;
     318        public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 6;
     319        public static final int TAG_WHITE_BALANCE = OFFSET + 7;
     320        public static final int TAG_SLOW_SHUTTER = OFFSET + 8;
     321        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 9;
     322        public static final int TAG_OPTICAL_ZOOM_CODE = OFFSET + 10;
     323        public static final int TAG_CAMERA_TEMPERATURE = OFFSET + 12;
     324        public static final int TAG_FLASH_GUIDE_NUMBER = OFFSET + 13;
     325        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 14;
     326        public static final int TAG_FLASH_EXPOSURE_BRACKETING = OFFSET + 15;
     327        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 16;
     328        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 17;
     329        public static final int TAG_CONTROL_MODE = OFFSET + 18;
     330        public static final int TAG_FOCUS_DISTANCE_UPPER = OFFSET + 19;
     331        public static final int TAG_FOCUS_DISTANCE_LOWER = OFFSET + 20;
     332        public static final int TAG_F_NUMBER = OFFSET + 21;
     333        public static final int TAG_EXPOSURE_TIME = OFFSET + 22;
     334        public static final int TAG_MEASURED_EV_2 = OFFSET + 23;
     335        public static final int TAG_BULB_DURATION = OFFSET + 24;
     336        public static final int TAG_CAMERA_TYPE = OFFSET + 26;
     337        public static final int TAG_AUTO_ROTATE = OFFSET + 27;
     338        public static final int TAG_ND_FILTER = OFFSET + 28;
     339        public static final int TAG_SELF_TIMER_2 = OFFSET + 29;
     340        public static final int TAG_FLASH_OUTPUT = OFFSET + 33;
     341    }
     342
     343    public final static class Panorama
     344    {
     345        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     346
     347        private static final int OFFSET = 0xC500;
     348
     349        public static final int TAG_PANORAMA_FRAME_NUMBER = OFFSET + 2;
     350        public static final int TAG_PANORAMA_DIRECTION = OFFSET + 5;
     351    }
     352
     353    public final static class AFInfo
     354    {
     355        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     356
     357        private static final int OFFSET = 0xD200;
     358
     359        public static final int TAG_NUM_AF_POINTS = OFFSET;
     360        public static final int TAG_VALID_AF_POINTS = OFFSET + 1;
     361        public static final int TAG_IMAGE_WIDTH = OFFSET + 2;
     362        public static final int TAG_IMAGE_HEIGHT = OFFSET + 3;
     363        public static final int TAG_AF_IMAGE_WIDTH = OFFSET + 4;
     364        public static final int TAG_AF_IMAGE_HEIGHT = OFFSET + 5;
     365        public static final int TAG_AF_AREA_WIDTH = OFFSET + 6;
     366        public static final int TAG_AF_AREA_HEIGHT = OFFSET + 7;
     367        public static final int TAG_AF_AREA_X_POSITIONS = OFFSET + 8;
     368        public static final int TAG_AF_AREA_Y_POSITIONS = OFFSET + 9;
     369        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 10;
     370        public static final int TAG_PRIMARY_AF_POINT_1 = OFFSET + 11;
     371        public static final int TAG_PRIMARY_AF_POINT_2 = OFFSET + 12; // not sure why there are two of these
     372    }
     373
     374//    /**
     375//     * Long Exposure Noise Reduction
     376//     * 0 = Off
     377//     * 1 = On
     378//     */
     379//    public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
     380//
     381//    /**
     382//     * Shutter/Auto Exposure-lock buttons
     383//     * 0 = AF/AE lock
     384//     * 1 = AE lock/AF
     385//     * 2 = AF/AF lock
     386//     * 3 = AE+release/AE+AF
     387//     */
     388//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
     389//
     390//    /**
     391//     * Mirror lockup
     392//     * 0 = Disable
     393//     * 1 = Enable
     394//     */
     395//    public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
     396//
     397//    /**
     398//     * Tv/Av and exposure level
     399//     * 0 = 1/2 stop
     400//     * 1 = 1/3 stop
     401//     */
     402//    public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
     403//
     404//    /**
     405//     * AF-assist light
     406//     * 0 = On (Auto)
     407//     * 1 = Off
     408//     */
     409//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
     410//
     411//    /**
     412//     * Shutter speed in Av mode
     413//     * 0 = Automatic
     414//     * 1 = 1/200 (fixed)
     415//     */
     416//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
     417//
     418//    /**
     419//     * Auto-Exposure Bracketting sequence/auto cancellation
     420//     * 0 = 0,-,+ / Enabled
     421//     * 1 = 0,-,+ / Disabled
     422//     * 2 = -,0,+ / Enabled
     423//     * 3 = -,0,+ / Disabled
     424//     */
     425//    public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307;
     426//
     427//    /**
     428//     * Shutter Curtain Sync
     429//     * 0 = 1st Curtain Sync
     430//     * 1 = 2nd Curtain Sync
     431//     */
     432//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
     433//
     434//    /**
     435//     * Lens Auto-Focus stop button Function Switch
     436//     * 0 = AF stop
     437//     * 1 = Operate AF
     438//     * 2 = Lock AE and start timer
     439//     */
     440//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
     441//
     442//    /**
     443//     * Auto reduction of fill flash
     444//     * 0 = Enable
     445//     * 1 = Disable
     446//     */
     447//    public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
     448//
     449//    /**
     450//     * Menu button return position
     451//     * 0 = Top
     452//     * 1 = Previous (volatile)
     453//     * 2 = Previous
     454//     */
     455//    public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
     456//
     457//    /**
     458//     * SET button function when shooting
     459//     * 0 = Not Assigned
     460//     * 1 = Change Quality
     461//     * 2 = Change ISO Speed
     462//     * 3 = Select Parameters
     463//     */
     464//    public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;
     465//
     466//    /**
     467//     * Sensor cleaning
     468//     * 0 = Disable
     469//     * 1 = Enable
     470//     */
     471//    public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;
    331472
    332473    // 9  A  B  C  D  E  F  10 11 12 13
    333474    // 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>();
    335477
    336478    static
    337479    {
    338         _tagNameMap.put(new Integer(TAG_CANON_FIRMWARE_VERSION), "Firmware Version");
    339         _tagNameMap.put(new Integer(TAG_CANON_IMAGE_NUMBER), "Image Number");
    340         _tagNameMap.put(new Integer(TAG_CANON_IMAGE_TYPE), "Image Type");
    341         _tagNameMap.put(new Integer(TAG_CANON_OWNER_NAME), "Owner Name");
    342         _tagNameMap.put(new Integer(TAG_CANON_UNKNOWN_1), "Makernote Unknown 1");
    343         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTIONS), "Custom Functions");
    344         _tagNameMap.put(new Integer(TAG_CANON_SERIAL_NUMBER), "Camera Serial Number");
    345         _tagNameMap.put(new Integer(TAG_CANON_STATE1_AF_POINT_SELECTED), "AF Point Selected");
    346         _tagNameMap.put(new Integer(TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE), "Continuous Drive Mode");
    347         _tagNameMap.put(new Integer(TAG_CANON_STATE1_CONTRAST), "Contrast");
    348         _tagNameMap.put(new Integer(TAG_CANON_STATE1_EASY_SHOOTING_MODE), "Easy Shooting Mode");
    349         _tagNameMap.put(new Integer(TAG_CANON_STATE1_EXPOSURE_MODE), "Exposure Mode");
    350         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_DETAILS), "Flash Details");
    351         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_MODE), "Flash Mode");
    352         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCAL_UNITS_PER_MM), "Focal Units per mm");
    353         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_MODE_1), "Focus Mode");
    354         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_MODE_2), "Focus Mode");
    355         _tagNameMap.put(new Integer(TAG_CANON_STATE1_IMAGE_SIZE), "Image Size");
    356         _tagNameMap.put(new Integer(TAG_CANON_STATE1_ISO), "Iso");
    357         _tagNameMap.put(new Integer(TAG_CANON_STATE1_LONG_FOCAL_LENGTH), "Long Focal Length");
    358         _tagNameMap.put(new Integer(TAG_CANON_STATE1_MACRO_MODE), "Macro Mode");
    359         _tagNameMap.put(new Integer(TAG_CANON_STATE1_METERING_MODE), "Metering Mode");
    360         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SATURATION), "Saturation");
    361         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SELF_TIMER_DELAY), "Self Timer Delay");
    362         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SHARPNESS), "Sharpness");
    363         _tagNameMap.put(new Integer(TAG_CANON_STATE1_SHORT_FOCAL_LENGTH), "Short Focal Length");
    364         _tagNameMap.put(new Integer(TAG_CANON_STATE1_QUALITY), "Quality");
    365         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_2), "Unknown Camera State 2");
    366         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_3), "Unknown Camera State 3");
    367         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_4), "Unknown Camera State 4");
    368         _tagNameMap.put(new Integer(TAG_CANON_STATE1_DIGITAL_ZOOM), "Digital Zoom");
    369         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_TYPE), "Focus Type");
    370         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_7), "Unknown Camera State 7");
    371         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_8), "Unknown Camera State 8");
    372         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_9), "Unknown Camera State 9");
    373         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_10), "Unknown Camera State 10");
    374         _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_ACTIVITY), "Flash Activity");
    375         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_12), "Unknown Camera State 12");
    376         _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_13), "Unknown Camera State 13");
    377         _tagNameMap.put(new Integer(TAG_CANON_STATE2_WHITE_BALANCE), "White Balance");
    378         _tagNameMap.put(new Integer(TAG_CANON_STATE2_SEQUENCE_NUMBER), "Sequence Number");
    379         _tagNameMap.put(new Integer(TAG_CANON_STATE2_AF_POINT_USED), "AF Point Used");
    380         _tagNameMap.put(new Integer(TAG_CANON_STATE2_FLASH_BIAS), "Flash Bias");
    381         _tagNameMap.put(new Integer(TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING), "Auto Exposure Bracketing");
    382         _tagNameMap.put(new Integer(TAG_CANON_STATE2_AEB_BRACKET_VALUE), "AEB Bracket Value");
    383         _tagNameMap.put(new Integer(TAG_CANON_STATE2_SUBJECT_DISTANCE), "Subject Distance");
    384 
    385         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION), "Long Exposure Noise Reduction");
    386         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS), "Shutter/Auto Exposure-lock Buttons");
    387         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP), "Mirror Lockup");
    388         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL), "Tv/Av And Exposure Level");
    389         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT), "AF-Assist Light");
    390         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE), "Shutter Speed in Av Mode");
    391         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING), "Auto-Exposure Bracketting Sequence/Auto Cancellation");
    392         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC), "Shutter Curtain Sync");
    393         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_STOP), "Lens Auto-Focus Stop Button Function Switch");
    394         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION), "Auto Reduction of Fill Flash");
    395         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN), "Menu Button Return Position");
    396         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION), "SET Button Function When Shooting");
    397         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING), "Sensor Cleaning");
     480        _tagNameMap.put(TAG_CANON_FIRMWARE_VERSION, "Firmware Version");
     481        _tagNameMap.put(TAG_CANON_IMAGE_NUMBER, "Image Number");
     482        _tagNameMap.put(TAG_CANON_IMAGE_TYPE, "Image Type");
     483        _tagNameMap.put(TAG_CANON_OWNER_NAME, "Owner Name");
     484        _tagNameMap.put(TAG_CANON_SERIAL_NUMBER, "Camera Serial Number");
     485        _tagNameMap.put(TAG_CAMERA_INFO_ARRAY, "Camera Info Array");
     486        _tagNameMap.put(TAG_CANON_FILE_LENGTH, "File Length");
     487        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTIONS_ARRAY, "Custom Functions");
     488        _tagNameMap.put(TAG_MODEL_ID, "Canon Model ID");
     489        _tagNameMap.put(TAG_MOVIE_INFO_ARRAY, "Movie Info Array");
     490
     491        _tagNameMap.put(CameraSettings.TAG_AF_POINT_SELECTED, "AF Point Selected");
     492        _tagNameMap.put(CameraSettings.TAG_CONTINUOUS_DRIVE_MODE, "Continuous Drive Mode");
     493        _tagNameMap.put(CameraSettings.TAG_CONTRAST, "Contrast");
     494        _tagNameMap.put(CameraSettings.TAG_EASY_SHOOTING_MODE, "Easy Shooting Mode");
     495        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode");
     496        _tagNameMap.put(CameraSettings.TAG_FLASH_DETAILS, "Flash Details");
     497        _tagNameMap.put(CameraSettings.TAG_FLASH_MODE, "Flash Mode");
     498        _tagNameMap.put(CameraSettings.TAG_FOCAL_UNITS_PER_MM, "Focal Units per mm");
     499        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_1, "Focus Mode");
     500        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_2, "Focus Mode");
     501        _tagNameMap.put(CameraSettings.TAG_IMAGE_SIZE, "Image Size");
     502        _tagNameMap.put(CameraSettings.TAG_ISO, "Iso");
     503        _tagNameMap.put(CameraSettings.TAG_LONG_FOCAL_LENGTH, "Long Focal Length");
     504        _tagNameMap.put(CameraSettings.TAG_MACRO_MODE, "Macro Mode");
     505        _tagNameMap.put(CameraSettings.TAG_METERING_MODE, "Metering Mode");
     506        _tagNameMap.put(CameraSettings.TAG_SATURATION, "Saturation");
     507        _tagNameMap.put(CameraSettings.TAG_SELF_TIMER_DELAY, "Self Timer Delay");
     508        _tagNameMap.put(CameraSettings.TAG_SHARPNESS, "Sharpness");
     509        _tagNameMap.put(CameraSettings.TAG_SHORT_FOCAL_LENGTH, "Short Focal Length");
     510        _tagNameMap.put(CameraSettings.TAG_QUALITY, "Quality");
     511        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_2, "Unknown Camera Setting 2");
     512        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_3, "Unknown Camera Setting 3");
     513        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_4, "Unknown Camera Setting 4");
     514        _tagNameMap.put(CameraSettings.TAG_DIGITAL_ZOOM, "Digital Zoom");
     515        _tagNameMap.put(CameraSettings.TAG_FOCUS_TYPE, "Focus Type");
     516        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_7, "Unknown Camera Setting 7");
     517        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_8, "Unknown Camera Setting 8");
     518        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_9, "Unknown Camera Setting 9");
     519        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_10, "Unknown Camera Setting 10");
     520        _tagNameMap.put(CameraSettings.TAG_FLASH_ACTIVITY, "Flash Activity");
     521        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_12, "Unknown Camera Setting 12");
     522        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_13, "Unknown Camera Setting 13");
     523
     524        _tagNameMap.put(FocalLength.TAG_WHITE_BALANCE, "White Balance");
     525        _tagNameMap.put(FocalLength.TAG_SEQUENCE_NUMBER, "Sequence Number");
     526        _tagNameMap.put(FocalLength.TAG_AF_POINT_USED, "AF Point Used");
     527        _tagNameMap.put(FocalLength.TAG_FLASH_BIAS, "Flash Bias");
     528        _tagNameMap.put(FocalLength.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
     529        _tagNameMap.put(FocalLength.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
     530        _tagNameMap.put(FocalLength.TAG_SUBJECT_DISTANCE, "Subject Distance");
     531
     532        _tagNameMap.put(ShotInfo.TAG_AUTO_ISO, "Auto ISO");
     533        _tagNameMap.put(ShotInfo.TAG_BASE_ISO, "Base ISO");
     534        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV, "Measured EV");
     535        _tagNameMap.put(ShotInfo.TAG_TARGET_APERTURE, "Target Aperture");
     536        _tagNameMap.put(ShotInfo.TAG_TARGET_EXPOSURE_TIME, "Target Exposure Time");
     537        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_COMPENSATION, "Exposure Compensation");
     538        _tagNameMap.put(ShotInfo.TAG_WHITE_BALANCE, "White Balance");
     539        _tagNameMap.put(ShotInfo.TAG_SLOW_SHUTTER, "Slow Shutter");
     540        _tagNameMap.put(ShotInfo.TAG_SEQUENCE_NUMBER, "Sequence Number");
     541        _tagNameMap.put(ShotInfo.TAG_OPTICAL_ZOOM_CODE, "Optical Zoom Code");
     542        _tagNameMap.put(ShotInfo.TAG_CAMERA_TEMPERATURE, "Camera Temperature");
     543        _tagNameMap.put(ShotInfo.TAG_FLASH_GUIDE_NUMBER, "Flash Guide Number");
     544        _tagNameMap.put(ShotInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus");
     545        _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");
    398642    }
    399643
     
    403647    }
    404648
     649    @NotNull
    405650    public String getName()
    406651    {
     
    408653    }
    409654
    410     protected HashMap getTagNameMap()
     655    @NotNull
     656    protected HashMap<Integer, String> getTagNameMap()
    411657    {
    412658        return _tagNameMap;
    413659    }
    414660
    415     /**
    416      * We need special handling for selected tags.
    417      * @param tagType
    418      * @param ints
    419      */
    420     public void setIntArray(int tagType, int[] ints)
    421     {
    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);
     661    @Override
     662    public void setIntArray(int tagType, @NotNull int[] ints)
     663    {
     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;
    446702        }
    447703    }
  • trunk/src/com/drew/metadata/exif/CasioType1MakernoteDescriptor.java

    r4231 r6127  
    11/*
    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 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
    24  *
     28 * Provides human-readable string representations of tag values stored in a <code>CasioType1MakernoteDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class CasioType1MakernoteDescriptor extends TagDescriptor
     32public class CasioType1MakernoteDescriptor extends TagDescriptor<CasioType1MakernoteDirectory>
    2733{
    28     public CasioType1MakernoteDescriptor(Directory directory)
     34    public CasioType1MakernoteDescriptor(@NotNull CasioType1MakernoteDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
     
    5966                return getCcdSensitivityDescription();
    6067            default:
    61                 return _directory.getString(tagType);
    62         }
    63     }
    64 
    65     public String getCcdSensitivityDescription() throws MetadataException
    66     {
    67         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY)) return null;
    68         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY);
     68                return super.getDescription(tagType);
     69        }
     70    }
     71
     72    @Nullable
     73    public String getCcdSensitivityDescription()
     74    {
     75        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY);
     76
     77        if (value == null)
     78            return null;
     79
    6980        switch (value) {
    7081            // these four for QV3000
     
    8798    }
    8899
    89     public String getSaturationDescription() throws MetadataException
    90     {
    91         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION)) return null;
    92         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION);
     100    @Nullable
     101    public String getSaturationDescription()
     102    {
     103        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION);
     104
     105        if (value == null)
     106            return null;
     107
    93108        switch (value) {
    94109            case 0:
     
    103118    }
    104119
    105     public String getContrastDescription() throws MetadataException
    106     {
    107         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST)) return null;
    108         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST);
     120    @Nullable
     121    public String getContrastDescription()
     122    {
     123        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST);
     124
     125        if (value == null)
     126            return null;
     127
    109128        switch (value) {
    110129            case 0:
     
    119138    }
    120139
    121     public String getSharpnessDescription() throws MetadataException
    122     {
    123         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS)) return null;
    124         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS);
     140    @Nullable
     141    public String getSharpnessDescription()
     142    {
     143        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS);
     144
     145        if (value == null)
     146            return null;
     147
    125148        switch (value) {
    126149            case 0:
     
    135158    }
    136159
    137     public String getDigitalZoomDescription() throws MetadataException
    138     {
    139         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM)) return null;
    140         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM);
     160    @Nullable
     161    public String getDigitalZoomDescription()
     162    {
     163        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM);
     164
     165        if (value == null)
     166            return null;
     167
    141168        switch (value) {
    142169            case 0x10000:
     
    153180    }
    154181
    155     public String getWhiteBalanceDescription() throws MetadataException
    156     {
    157         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE)) return null;
    158         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE);
     182    @Nullable
     183    public String getWhiteBalanceDescription()
     184    {
     185        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE);
     186
     187        if (value == null)
     188            return null;
     189
    159190        switch (value) {
    160191            case 1:
     
    165196                return "Daylight";
    166197            case 4:
    167                 return "Flourescent";
     198                return "Florescent";
    168199            case 5:
    169200                return "Shade";
     
    175206    }
    176207
    177     public String getObjectDistanceDescription() throws MetadataException
    178     {
    179         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE)) return null;
    180         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE);
     208    @Nullable
     209    public String getObjectDistanceDescription()
     210    {
     211        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE);
     212
     213        if (value == null)
     214            return null;
     215
    181216        return value + " mm";
    182217    }
    183218
    184     public String getFlashIntensityDescription() throws MetadataException
    185     {
    186         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY)) return null;
    187         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY);
     219    @Nullable
     220    public String getFlashIntensityDescription()
     221    {
     222        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY);
     223
     224        if (value == null)
     225            return null;
     226
    188227        switch (value) {
    189228            case 11:
     
    198237    }
    199238
    200     public String getFlashModeDescription() throws MetadataException
    201     {
    202         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE)) return null;
    203         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE);
     239    @Nullable
     240    public String getFlashModeDescription()
     241    {
     242        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE);
     243
     244        if (value == null)
     245            return null;
     246
    204247        switch (value) {
    205248            case 1:
     
    218261    }
    219262
    220     public String getFocusingModeDescription() throws MetadataException
    221     {
    222         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE)) return null;
    223         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE);
     263    @Nullable
     264    public String getFocusingModeDescription()
     265    {
     266        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE);
     267
     268        if (value == null)
     269            return null;
     270
    224271        switch (value) {
    225272            case 2:
     
    236283    }
    237284
    238     public String getQualityDescription() throws MetadataException
    239     {
    240         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY)) return null;
    241         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY);
     285    @Nullable
     286    public String getQualityDescription()
     287    {
     288        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY);
     289
     290        if (value == null)
     291            return null;
     292
    242293        switch (value) {
    243294            case 1:
     
    252303    }
    253304
    254     public String getRecordingModeDescription() throws MetadataException
    255     {
    256         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE)) return null;
    257         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE);
     305    @Nullable
     306    public String getRecordingModeDescription()
     307    {
     308        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE);
     309
     310        if (value == null)
     311            return null;
     312
    258313        switch (value) {
    259314            case 1:
  • trunk/src/com/drew/metadata/exif/CasioType1MakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Casio (type 1) cameras.
     30 *
    2431 * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment.
    2532 * Makernote data begins immediately (no header).
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2635 */
    2736public class CasioType1MakernoteDirectory extends Directory
     
    4857    public static final int TAG_CASIO_CCD_SENSITIVITY = 0x0014;
    4958
    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>();
    5161
    5262    static
    5363    {
    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");
    7484    }
    7585
     
    7989    }
    8090
     91    @NotNull
    8192    public String getName()
    8293    {
     
    8495    }
    8596
    86     protected HashMap getTagNameMap()
     97    @NotNull
     98    protected HashMap<Integer, String> getTagNameMap()
    8799    {
    88         return tagNameMap;
     100        return _tagNameMap;
    89101    }
    90102}
  • trunk/src/com/drew/metadata/exif/CasioType2MakernoteDescriptor.java

    r4231 r6127  
    11/*
    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 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
    24  *
     28 * Provides human-readable string representations of tag values stored in a <code>CasioType2MakernoteDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class CasioType2MakernoteDescriptor extends TagDescriptor
     32public class CasioType2MakernoteDescriptor extends TagDescriptor<CasioType2MakernoteDirectory>
    2733{
    28     public CasioType2MakernoteDescriptor(Directory directory)
     34    public CasioType2MakernoteDescriptor(@NotNull CasioType2MakernoteDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
     
    9198                return getFilterDescription();
    9299            default:
    93                 return _directory.getString(tagType);
    94         }
    95     }
    96 
    97     public String getFilterDescription() throws MetadataException
    98     {
    99         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER)) return null;
    100         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER);
    101         switch (value) {
    102             case 0:
    103                 return "Off";
    104             default:
    105                 return "Unknown (" + value + ")";
    106         }
    107     }
    108 
    109     public String getEnhancementDescription() throws MetadataException
    110     {
    111         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT)) return null;
    112         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT);
    113         switch (value) {
    114             case 0:
    115                 return "Off";
    116             default:
    117                 return "Unknown (" + value + ")";
    118         }
    119     }
    120 
    121     public String getColourModeDescription() throws MetadataException
    122     {
    123         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE)) return null;
    124         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE);
    125         switch (value) {
    126             case 0:
    127                 return "Off";
    128             default:
    129                 return "Unknown (" + value + ")";
    130         }
    131     }
    132 
    133     public String getCcdIsoSensitivityDescription() throws MetadataException
    134     {
    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);
     100                return super.getDescription(tagType);
     101        }
     102    }
     103
     104    @Nullable
     105    public String getFilterDescription()
     106    {
     107        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER);
     108        if (value==null)
     109            return null;
     110        switch (value) {
     111            case 0:
     112                return "Off";
     113            default:
     114                return "Unknown (" + value + ")";
     115        }
     116    }
     117
     118    @Nullable
     119    public String getEnhancementDescription()
     120    {
     121        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT);
     122        if (value==null)
     123            return null;
     124        switch (value) {
     125            case 0:
     126                return "Off";
     127            default:
     128                return "Unknown (" + value + ")";
     129        }
     130    }
     131
     132    @Nullable
     133    public String getColourModeDescription()
     134    {
     135        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE);
     136        if (value==null)
     137            return null;
     138        switch (value) {
     139            case 0:
     140                return "Off";
     141            default:
     142                return "Unknown (" + value + ")";
     143        }
     144    }
     145
     146    @Nullable
     147    public String getCcdIsoSensitivityDescription()
     148    {
     149        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY);
     150        if (value==null)
     151            return null;
    137152        switch (value) {
    138153            case 0:
     
    145160    }
    146161
    147     public String getBestShotModeDescription() throws MetadataException
    148     {
    149         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE)) return null;
    150         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE);
    151         switch (value) {
    152             default:
    153                 return "Unknown (" + value + ")";
    154         }
    155     }
    156 
     162    @Nullable
     163    public String getBestShotModeDescription()
     164    {
     165        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE);
     166        if (value==null)
     167            return null;
     168        switch (value) {
     169            default:
     170                return "Unknown (" + value + ")";
     171        }
     172    }
     173
     174    @Nullable
    157175    public String getTimeZoneDescription()
    158176    {
    159         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_TIME_ZONE)) return null;
    160177        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_TIME_ZONE);
    161178    }
    162179
    163     public String getFocusMode2Description() throws MetadataException
    164     {
    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);
     180    @Nullable
     181    public String getFocusMode2Description()
     182    {
     183        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2);
     184        if (value==null)
     185            return null;
    167186        switch (value) {
    168187            case 1:
     
    175194    }
    176195
    177     public String getQualityDescription() throws MetadataException
    178     {
    179         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY)) return null;
    180         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY);
     196    @Nullable
     197    public String getQualityDescription()
     198    {
     199        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY);
     200        if (value==null)
     201            return null;
    181202        switch (value) {
    182203            case 3:
     
    187208    }
    188209
    189     public String getSelfTimerDescription() throws MetadataException
    190     {
    191         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER)) return null;
    192         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER);
    193         switch (value) {
    194             case 1:
    195                 return "Off";
    196             default:
    197                 return "Unknown (" + value + ")";
    198         }
    199     }
    200 
    201     public String getRecordModeDescription() throws MetadataException
    202     {
    203         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE)) return null;
    204         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE);
     210    @Nullable
     211    public String getSelfTimerDescription()
     212    {
     213        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER);
     214        if (value==null)
     215            return null;
     216        switch (value) {
     217            case 1:
     218                return "Off";
     219            default:
     220                return "Unknown (" + value + ")";
     221        }
     222    }
     223
     224    @Nullable
     225    public String getRecordModeDescription()
     226    {
     227        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE);
     228        if (value==null)
     229            return null;
    205230        switch (value) {
    206231            case 2:
     
    211236    }
    212237
    213     public String getFlashDistanceDescription() throws MetadataException
    214     {
    215         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE)) return null;
    216         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE);
    217         switch (value) {
    218             case 0:
    219                 return "Off";
    220             default:
    221                 return "Unknown (" + value + ")";
    222         }
    223     }
    224 
    225     public String getObjectDistanceDescription() throws MetadataException
    226     {
    227         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE)) return null;
    228         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE);
     238    @Nullable
     239    public String getFlashDistanceDescription()
     240    {
     241        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE);
     242        if (value==null)
     243            return null;
     244        switch (value) {
     245            case 0:
     246                return "Off";
     247            default:
     248                return "Unknown (" + value + ")";
     249        }
     250    }
     251
     252    @Nullable
     253    public String getObjectDistanceDescription()
     254    {
     255        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE);
     256        if (value==null)
     257            return null;
    229258        return Integer.toString(value) + " mm";
    230259    }
    231260
    232     public String getWhiteBalance2Description() throws MetadataException
    233     {
    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);
     261    @Nullable
     262    public String getWhiteBalance2Description()
     263    {
     264        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2);
     265        if (value==null)
     266            return null;
    236267        switch (value) {
    237268            case 0:
     
    248279    }
    249280
     281    @Nullable
    250282    public String getWhiteBalanceBiasDescription()
    251283    {
    252         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS)) return null;
    253284        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS);
    254285    }
    255286
    256     public String getCasioPreviewThumbnailDescription() throws MetadataException
    257     {
    258         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL)) return null;
     287    @Nullable
     288    public String getCasioPreviewThumbnailDescription()
     289    {
    259290        final byte[] bytes = _directory.getByteArray(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL);
     291        if (bytes==null)
     292            return null;
    260293        return "<" + bytes.length + " bytes of image data>";
    261294    }
    262295
     296    @Nullable
    263297    public String getPrintImageMatchingInfoDescription()
    264298    {
    265299        // 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;
    267300        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO);
    268301    }
    269302
    270     public String getSharpnessDescription() throws MetadataException
    271     {
    272         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS)) return null;
    273         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS);
     303    @Nullable
     304    public String getSharpnessDescription()
     305    {
     306        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS);
     307        if (value==null)
     308            return null;
    274309        switch (value) {
    275310            case 0:
     
    284319    }
    285320
    286     public String getContrastDescription() throws MetadataException
    287     {
    288         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST)) return null;
    289         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST);
     321    @Nullable
     322    public String getContrastDescription()
     323    {
     324        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST);
     325        if (value==null)
     326            return null;
    290327        switch (value) {
    291328            case 0:
     
    300337    }
    301338
    302     public String getSaturationDescription() throws MetadataException
    303     {
    304         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION)) return null;
    305         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION);
     339    @Nullable
     340    public String getSaturationDescription()
     341    {
     342        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION);
     343        if (value==null)
     344            return null;
    306345        switch (value) {
    307346            case 0:
     
    316355    }
    317356
    318     public String getFocalLengthDescription() throws MetadataException
    319     {
    320         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH)) return null;
    321         double value = _directory.getDouble(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH);
     357    @Nullable
     358    public String getFocalLengthDescription()
     359    {
     360        Double value = _directory.getDoubleObject(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH);
     361        if (value==null)
     362            return null;
    322363        return Double.toString(value / 10d) + " mm";
    323364    }
    324365
    325     public String getWhiteBalance1Description() throws MetadataException
    326     {
    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);
     366    @Nullable
     367    public String getWhiteBalance1Description()
     368    {
     369        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1);
     370        if (value==null)
     371            return null;
    329372        switch (value) {
    330373            case 0:
     
    337380                return "Tungsten";
    338381            case 4:
    339                 return "Flourescent";
     382                return "Florescent";
    340383            case 5:
    341384                return "Manual";
     
    345388    }
    346389
    347     public String getIsoSensitivityDescription() throws MetadataException
    348     {
    349         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY)) return null;
    350         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY);
     390    @Nullable
     391    public String getIsoSensitivityDescription()
     392    {
     393        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY);
     394        if (value==null)
     395            return null;
    351396        switch (value) {
    352397            case 3:
     
    363408    }
    364409
    365     public String getFocusMode1Description() throws MetadataException
    366     {
    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);
     410    @Nullable
     411    public String getFocusMode1Description()
     412    {
     413        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1);
     414        if (value==null)
     415            return null;
    369416        switch (value) {
    370417            case 0:
     
    377424    }
    378425
    379     public String getImageSizeDescription() throws MetadataException
    380     {
    381         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE)) return null;
    382         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE);
     426    @Nullable
     427    public String getImageSizeDescription()
     428    {
     429        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE);
     430        if (value==null)
     431            return null;
    383432        switch (value) {
    384433            case 0:  return "640 x 480 pixels";
     
    393442    }
    394443
    395     public String getQualityModeDescription() throws MetadataException
    396     {
    397         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE)) return null;
    398         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE);
     444    @Nullable
     445    public String getQualityModeDescription()
     446    {
     447        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE);
     448        if (value==null)
     449            return null;
    399450        switch (value) {
    400451            case 1:
     
    407458    }
    408459
     460    @Nullable
    409461    public String getThumbnailOffsetDescription()
    410462    {
    411         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_OFFSET)) return null;
    412463        return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_OFFSET);
    413464    }
    414465
    415     public String getThumbnailSizeDescription() throws MetadataException
    416     {
    417         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE)) return null;
    418         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE);
     466    @Nullable
     467    public String getThumbnailSizeDescription()
     468    {
     469        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE);
     470        if (value==null)
     471            return null;
    419472        return Integer.toString(value) + " bytes";
    420473    }
    421474
    422     public String getThumbnailDimensionsDescription() throws MetadataException
    423     {
    424         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS)) return null;
     475    @Nullable
     476    public String getThumbnailDimensionsDescription()
     477    {
    425478        int[] dimensions = _directory.getIntArray(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS);
    426         if (dimensions.length!=2)
     479        if (dimensions==null || dimensions.length!=2)
    427480            return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS);
    428481        return dimensions[0] + " x " + dimensions[1] + " pixels";
  • trunk/src/com/drew/metadata/exif/CasioType2MakernoteDirectory.java

    r4231 r6127  
    11/*
    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 27-Nov-2002 10:10:47 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Casio (type 2) cameras.
     30 *
    2431 * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment.
    2532 * Makernote data begins after a 6-byte header: "QVC\x00\x00\x00"
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2635 */
    2736public class CasioType2MakernoteDirectory extends Directory
     
    167176    public static final int TAG_CASIO_TYPE2_FILTER = 0x3017;
    168177
    169     protected static final HashMap tagNameMap = new HashMap();
     178    @NotNull
     179    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    170180
    171181    static
    172182    {
    173         // TODO add names
    174         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");
    202212    }
    203213
     
    207217    }
    208218
     219    @NotNull
    209220    public String getName()
    210221    {
     
    212223    }
    213224
    214     protected HashMap getTagNameMap()
    215     {
    216         return tagNameMap;
     225    @NotNull
     226    protected HashMap<Integer, String> getTagNameMap()
     227    {
     228        return _tagNameMap;
    217229    }
    218230}
  • trunk/src/com/drew/metadata/exif/DataFormat.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
     23import com.drew.lang.annotations.NotNull;
    1724import com.drew.metadata.MetadataException;
    1825
    1926/**
    2027 * An enumeration of data formats used in the TIFF IFDs.
     28 *
     29 * @author Drew Noakes http://drewnoakes.com
    2130 */
    2231public class DataFormat
    2332{
    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);
     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);
    3645
    37     private final String myName;
    38     private final int value;
     46    @NotNull private final String _name;
     47    private final int _value;
    3948
     49    @NotNull
    4050    public static DataFormat fromValue(int value) throws MetadataException
    4151    {
     
    5969    }
    6070
    61     private DataFormat(String name, int value)
     71    private DataFormat(@NotNull String name, int value)
    6272    {
    63         myName = name;
    64         this.value = value;
     73        _name = name;
     74        _value = value;
    6575    }
    6676
    6777    public int getValue()
    6878    {
    69         return value;
     79        return _value;
    7080    }
    7181
     82    @NotNull
    7283    public String toString()
    7384    {
    74         return myName;
     85        return _name;
    7586    }
    7687}
  • trunk/src/com/drew/metadata/exif/ExifInteropDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2125import com.drew.metadata.TagDescriptor;
    2226
    2327/**
     28 * Provides human-readable string representations of tag values stored in a <code>ExifInteropDirectory</code>.
    2429 *
     30 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class ExifInteropDescriptor extends TagDescriptor
     32public class ExifInteropDescriptor extends TagDescriptor<ExifInteropDirectory>
    2733{
    28     public ExifInteropDescriptor(Directory directory)
     34    public ExifInteropDescriptor(@NotNull ExifInteropDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3441    {
    3542        switch (tagType) {
     
    3946                return getInteropVersionDescription();
    4047            default:
    41                 return _directory.getString(tagType);
     48                return super.getDescription(tagType);
    4249        }
    4350    }
    4451
    45     public String getInteropVersionDescription() throws MetadataException
     52    @Nullable
     53    public String getInteropVersionDescription()
    4654    {
    47         if (!_directory.containsTag(ExifInteropDirectory.TAG_INTEROP_VERSION)) return null;
    4855        int[] ints = _directory.getIntArray(ExifInteropDirectory.TAG_INTEROP_VERSION);
    49         return ExifDescriptor.convertBytesToVersionString(ints);
     56        return convertBytesToVersionString(ints, 2);
    5057    }
    5158
     59    @Nullable
    5260    public String getInteropIndexDescription()
    5361    {
    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 + ")";
    6170    }
    6271}
  • trunk/src/com/drew/metadata/exif/ExifInteropDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes Exif interoperability tags.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class ExifInteropDirectory extends Directory
     
    3239    public static final int TAG_RELATED_IMAGE_LENGTH = 0x1002;
    3340
    34     protected static final HashMap tagNameMap;
     41    @NotNull
     42    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    3543
    3644    static
    3745    {
    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");
    4451    }
    4552
     
    4956    }
    5057
     58    @NotNull
    5159    public String getName()
    5260    {
     
    5462    }
    5563
    56     protected HashMap getTagNameMap()
     64    @NotNull
     65    protected HashMap<Integer, String> getTagNameMap()
    5766    {
    58         return tagNameMap;
     67        return _tagNameMap;
    5968    }
    6069}
  • trunk/src/com/drew/metadata/exif/ExifReader.java

    r4231 r6127  
    11/*
    2  * EXIFExtractor.java
     2 * Copyright 2002-2012 Drew Noakes
    33 *
    4  * This class based upon code from Jhead, a C program for extracting and
    5  * 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
    77 *
    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
    139 *
    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.
    1815 *
    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:
    2317 *
    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/
    4320 */
    4421package com.drew.metadata.exif;
    4522
    46 import com.drew.imaging.jpeg.JpegProcessingException;
    47 import com.drew.imaging.jpeg.JpegSegmentData;
    48 import com.drew.imaging.jpeg.JpegSegmentReader;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
    4925import com.drew.lang.Rational;
     26import com.drew.lang.annotations.NotNull;
    5027import com.drew.metadata.Directory;
    5128import com.drew.metadata.Metadata;
    5229import com.drew.metadata.MetadataReader;
    5330
    54 import java.io.File;
    55 import java.io.InputStream;
    56 import java.util.HashMap;
     31import java.util.HashSet;
     32import java.util.Set;
    5733
    5834/**
    59  * Extracts Exif data from a JPEG header segment, providing information about the
    60  * camera/scanner/capture device (if available).  Information is encapsulated in
    61  * an <code>Metadata</code> object.
    62  * @author  Drew Noakes http://drewnoakes.com
     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
    6339 */
    6440public class ExifReader implements MetadataReader
    6541{
    66     /**
    67      * The JPEG segment as an array of bytes.
    68      */
    69     private final byte[] _data;
    70 
    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      */
     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 };
     47
     48    /** The number of formats known. */
    9249    private static final int MAX_FORMAT_CODE = 12;
    9350
    9451    // 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. */
    9754    private static final int FMT_BYTE = 1;
     55    /** A fixed-length character string. */
    9856    private static final int FMT_STRING = 2;
     57    /** An unsigned 16-bit integer. */
    9958    private static final int FMT_USHORT = 3;
     59    /** An unsigned 32-bit integer. */
    10060    private static final int FMT_ULONG = 4;
    10161    private static final int FMT_URATIONAL = 5;
     62    /** An 8-bit signed integer. */
    10263    private static final int FMT_SBYTE = 6;
    10364    private static final int FMT_UNDEFINED = 7;
     65    /** A signed 16-bit integer. */
    10466    private static final int FMT_SSHORT = 8;
     67    /** A signed 32-bit integer. */
    10568    private static final int FMT_SLONG = 9;
    10669    private static final int FMT_SRATIONAL = 10;
     70    /** A 32-bit floating point number. */
    10771    private static final int FMT_SINGLE = 11;
     72    /** A 64-bit floating point number. */
    10873    private static final int FMT_DOUBLE = 12;
    10974
    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. */
    11178    public static final int TAG_INTEROP_OFFSET = 0xA005;
     79    /** This tag is a pointer to the Exif GPS IFD. */
    11280    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;
    11483
    11584    public static final int TIFF_HEADER_START_OFFSET = 6;
    116 
    117     /**
    118      * Creates an ExifReader for a JpegSegmentData object.
    119      * @param segmentData
    120      */
    121     public ExifReader(JpegSegmentData segmentData)
    122     {
    123         this(segmentData.getSegment(JpegSegmentReader.SEGMENT_APP1));
    124     }
    125 
    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     }
    135 
    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     }
    144 
    145     /**
    146      * Creates an ExifReader for the given JPEG header segment.
    147      */
    148     public ExifReader(byte[] data)
    149     {
    150         _data = data;
    151     }
    152 
    153     /**
    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     }
    16085
    16186    /**
    16287     * Performs the Exif data extraction, adding found values to the specified
    16388     * 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.
    16492     */
    165     public Metadata extract(Metadata metadata)
    166     {
    167         _metadata = metadata;
    168         if (_data==null)
    169             return _metadata;
    170 
    171         // once we know there's some data, create the directory and start working on it
    172         ExifDirectory directory = (ExifDirectory)_metadata.getDirectory(ExifDirectory.class);
     93    public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
     94    {
     95        final ExifSubIFDDirectory directory = metadata.getOrCreateDirectory(ExifSubIFDDirectory.class);
    17396
    17497        // check for the header length
    175         if (_data.length<=14) {
     98        if (reader.getLength() <= 14) {
    17699            directory.addError("Exif data segment must contain at least 14 bytes");
    177             return _metadata;
     100            return;
    178101        }
    179102
    180103        // 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         }
    185 
     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            }
     109
     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        }
     114    }
     115
     116    /**
     117     * Performs the Exif data extraction on a TIFF/RAW, adding found values to the specified
     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.
     122     */
     123    public void extractTiff(@NotNull BufferReader reader, @NotNull Metadata metadata)
     124    {
     125        final ExifIFD0Directory directory = metadata.getOrCreateDirectory(ExifIFD0Directory.class);
     126
     127        try {
     128            extractIFD(metadata, directory, 0, reader);
     129        } catch (BufferBoundsException e) {
     130            directory.addError("Exif data segment ended prematurely");
     131        }
     132    }
     133
     134    private void extractIFD(@NotNull Metadata metadata, @NotNull final ExifIFD0Directory directory, int tiffHeaderOffset, @NotNull BufferReader reader) throws BufferBoundsException
     135    {
    186136        // this should be either "MM" or "II"
    187         String byteOrderIdentifier = new String(_data, 6, 2);
    188         if (!setByteOrder(byteOrderIdentifier)) {
     137        String byteOrderIdentifier = reader.getString(tiffHeaderOffset, 2);
     138
     139        if ("MM".equals(byteOrderIdentifier)) {
     140            reader.setMotorolaByteOrder(true);
     141        } else if ("II".equals(byteOrderIdentifier)) {
     142            reader.setMotorolaByteOrder(false);
     143        } else {
    189144            directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
    190             return _metadata;
     145            return;
    191146        }
    192147
    193148        // 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;
    197         }
    198 
    199         int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET;
    200 
    201         // David Ekholm sent an digital camera image that has this problem
    202         if (firstDirectoryOffset>=_data.length - 1) {
     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;
     158        }
     159
     160        int firstDirectoryOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset;
     161
     162        // David Ekholm sent a digital camera image that has this problem
     163        if (firstDirectoryOffset >= reader.getLength() - 1) {
    203164            directory.addError("First exif directory offset is beyond end of Exif data segment");
    204165            // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
     
    206167        }
    207168
    208         HashMap processedDirectoryOffsets = new HashMap();
    209 
    210         // 0th IFD (we merge with Exif IFD)
    211         processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET);
     169        Set<Integer> processedDirectoryOffsets = new HashSet<Integer>();
     170
     171        processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, tiffHeaderOffset, metadata, reader);
    212172
    213173        // 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];
    234             }
    235             exifDirectory.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);
    236         } catch (Throwable e) {
    237             exifDirectory.addError("Unable to extract thumbnail: " + e.getMessage());
    238         }
    239     }
    240 
    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;
     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                }
     185            }
     186        }
    251187    }
    252188
    253189    /**
    254190     * Process one of the nested Tiff IFD directories.
     191     * <p/>
     192     * Header
    255193     * 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
    260199     */
    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
    262201    {
    263202        // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
    264         if (processedDirectoryOffsets.containsKey(new Integer(dirStartOffset)))
     203        if (processedDirectoryOffsets.contains(Integer.valueOf(dirStartOffset)))
    265204            return;
    266205
    267206        // remember that we've visited this directory so that we don't visit it again later
    268         processedDirectoryOffsets.put(new Integer(dirStartOffset), "processed");
    269 
    270         if (dirStartOffset>=_data.length || dirStartOffset<0) {
    271             directory.addError("Ignored directory marked to start outside data segement");
    272             return;
    273         }
    274 
    275         if (!isDirectoryLengthValid(dirStartOffset, tiffHeaderOffset)) {
     207        processedDirectoryOffsets.add(dirStartOffset);
     208
     209        if (dirStartOffset >= reader.getLength() || dirStartOffset < 0) {
     210            directory.addError("Ignored directory marked to start outside data segment");
     211            return;
     212        }
     213
     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()) {
    276219            directory.addError("Illegally sized directory");
    277220            return;
    278221        }
    279222
    280         // First two bytes in the IFD are the number of tags in this directory
    281         int dirTagCount = get16Bits(dirStartOffset);
    282 
    283223        // Handle each tag in this directory
    284         for (int tagNumber = 0; tagNumber<dirTagCount; tagNumber++)
    285         {
     224        for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) {
    286225            final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber);
    287226
    288227            // 2 bytes for the tag type
    289             final int tagType = get16Bits(tagOffset);
     228            final int tagType = reader.getUInt16(tagOffset);
    290229
    291230            // 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;
    296237            }
    297238
    298239            // 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");
    302243                continue;
    303244            }
    304245            // each component may have more than one byte... calculate the total number of bytes
    305246            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");
    309266                continue;
    310267            }
     
    312269            // Check that this tag isn't going to allocate outside the bounds of the data array.
    313270            // This addresses an uncommon OutOfMemoryError.
    314             if (byteCount < 0 || tagValueOffset + byteCount > _data.length)
    315             {
     271            if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) {
    316272                directory.addError("Illegal number of bytes: " + byteCount);
    317273                continue;
    318274            }
    319275
    320             // Calculate the value as an offset for cases where the tag represents directory
    321             final int subdirOffset = tiffHeaderOffset + get32Bits(tagValueOffset);
    322 
    323276            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);
    326280                    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);
    329285                    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);
    332290                    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);
    335294                    continue;
    336                 default:
    337                     processTag(directory, tagType, tagValueOffset, componentCount, formatCode);
     295                }
     296                default: {
     297                    processTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader);
    338298                    break;
     299                }
    339300            }
    340301        }
     
    342303        // at the end of each IFD is an optional link to the next IFD
    343304        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) {
    346307            nextDirectoryOffset += tiffHeaderOffset;
    347             if (nextDirectoryOffset>=_data.length) {
     308            if (nextDirectoryOffset >= reader.getLength()) {
    348309                // Last 4 bytes of IFD reference another IFD with an address that is out of bounds
    349310                // Note this could have been caused by jhead 1.3 cropping too much
     
    353314                return;
    354315            }
    355             // the next directory is of same type as this one
    356             processDirectory(directory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset);
    357         }
    358     }
    359 
    360     private void processMakerNote(int subdirOffset, HashMap processedDirectoryOffsets, int 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);
     319        }
     320    }
     321
     322    private void processMakerNote(int subdirOffset, @NotNull Set<Integer> processedDirectoryOffsets, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull BufferReader reader) throws BufferBoundsException
    361323    {
    362324        // Determine the camera model and makernote format
    363         Directory exifDirectory = _metadata.getDirectory(ExifDirectory.class);
    364 
    365         if (exifDirectory==null)
    366             return;
    367 
    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         {
     325        Directory ifd0Directory = metadata.getDirectory(ExifIFD0Directory.class);
     326
     327        if (ifd0Directory==null)
     328            return;
     329
     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)) {
    378342            // 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)) {
    387347                /* There are two scenarios here:
    388348                 * Type 1:                  **
     
    393353                 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
    394354                 */
    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             }
     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 {
     367                // The IFD begins with the first MakerNote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
     368                processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
     369            }
     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")) {
     386            if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
     387                processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset, metadata, reader);
    402388            else
    403             {
    404                 // 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);
    406             }
    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         {
    422             if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
    423                 processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset);
    424             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();
    431392            // bug in fujifilm makernote ifd means we temporarily use Intel byte ordering
    432             _isMotorollaByteOrder = false;
     393            reader.setMotorolaByteOrder(false);
    433394            // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
    434395            // IFD, though the offset is relative to the start of the makernote, not the TIFF
    435396            // 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")) {
    442401            // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
    443402            // 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)) {
    455405            // 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))) {
    460408            // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
    461409            // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
    462410            // 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)) {
    467413            // NON-Standard TIFF IFD Data using Casio Type 2 Tags
    468414            // IFD has no Next-IFD pointer at end of IFD, and
     
    470416            // Observed for:
    471417            // - 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"))) {
    476420            // NON-Standard TIFF IFD Data using Pentax Tags
    477421            // IFD has no Next-IFD pointer at end of IFD, and
     
    480424            // - PENTAX Optio 330
    481425            // - 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 {
    486433            // TODO how to store makernote data when it's not from a supported camera model?
    487434            // this is difficult as the starting offset is not known.  we could look for it...
    488             exifDirectory.addError("Unsupported makernote data ignored.");
    489         }
    490     }
    491 
    492     private boolean isDirectoryLengthValid(int dirStartOffset, int tiffHeaderOffset)
    493     {
    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 this
    498             return false;
    499         }
    500         return true;
    501     }
    502 
    503     private void processTag(Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode)
     435        }
     436    }
     437
     438    private void processTag(@NotNull Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, @NotNull final BufferReader reader) throws BufferBoundsException
    504439    {
    505440        // Directory simply stores raw values
    506441        // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions
    507         switch (formatCode)
    508         {
     442        switch (formatCode) {
    509443            case FMT_UNDEFINED:
    510444                // 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));
    516446                break;
    517447            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*/
    519474                break;
    520475            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;
    521485            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) {
    526489                    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)));
    529492                    directory.setRationalArray(tagType, rationals);
    530493                }
    531494                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
    532520            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;
    533530            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));
    538533                } else {
    539534                    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);
    542537                    directory.setIntArray(tagType, bytes);
    543538                }
    544539                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);
    549543                    directory.setInt(tagType, i);
    550544                } else {
    551545                    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));
    554548                    directory.setIntArray(tagType, ints);
    555549                }
    556550                break;
    557             case FMT_USHORT:
    558551            case FMT_SSHORT:
    559                 if (componentCount==1) {
    560                     int i = get16Bits(tagValueOffset);
     552                if (componentCount == 1) {
     553                    int i = reader.getInt16(tagValueOffset);
    561554                    directory.setInt(tagType, i);
    562555                } else {
    563556                    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));
    566559                    directory.setIntArray(tagType, ints);
    567560                }
     
    569562            case FMT_SLONG:
    570563            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);
    573567                    directory.setInt(tagType, i);
    574568                } else {
    575569                    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));
    578572                    directory.setIntArray(tagType, ints);
    579573                }
     
    584578    }
    585579
    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 an
    591             // 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 value
    595                 return -1; // signal error
    596             }
    597             return tiffHeaderOffset + offsetVal;
    598         } else {
    599             // 4 bytes or less and value is in the dir entry itself
    600             return dirEntryOffset + 8;
    601         }
    602     }
    603 
    604     /**
    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 
    617580    /**
    618581     * Determine the offset at which a given InteropArray entry begins within the specified IFD.
     582     *
    619583     * @param dirStartOffset the offset at which the IFD starts
    620      * @param entryNumber the zero-based entry number
     584     * @param entryNumber    the zero-based entry number
    621585     */
    622586    private int calculateTagOffset(int dirStartOffset, int entryNumber)
     
    626590        return dirStartOffset + 2 + (12 * entryNumber);
    627591    }
    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 first
    639             return (_data[offset] << 8 & 0xFF00) | (_data[offset + 1] & 0xFF);
    640         } else {
    641             // Intel ordering - LSB first
    642             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 first
    656             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 first
    662             return (_data[offset + 3] << 24 & 0xFF000000) |
    663                     (_data[offset + 2] << 16 & 0xFF0000) |
    664                     (_data[offset + 1] << 8 & 0xFF00) |
    665                     (_data[offset] & 0xFF);
    666         }
    667     }
    668592}
  • trunk/src/com/drew/metadata/exif/FujifilmMakernoteDescriptor.java

    r4231 r6127  
    11/*
    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 27-Nov-2002 10:12:05 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    1923import com.drew.lang.Rational;
    20 import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
    2226import com.drew.metadata.TagDescriptor;
    2327
    2428/**
     29 * Provides human-readable string representations of tag values stored in a <code>FujifilmMakernoteDirectory</code>.
     30 * <p/>
    2531 * Fujifilm's digicam added the MakerNote tag from the Year2000's model (e.g.Finepix1400,
    2632 * Finepix4700). It uses IFD format and start from ASCII character 'FUJIFILM', and next 4
    2733 * bytes(value 0x000c) points the offset to first IFD entry. Example of actual data
    2834 * structure is shown below.
    29  *
     35 * <p/>
     36 * <pre><code>
    3037 * :0000: 46 55 4A 49 46 49 4C 4D-0C 00 00 00 0F 00 00 00 :0000: FUJIFILM........
    3138 * :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/>
    3341 * There are two big differences to the other manufacturers.
    3442 * - Fujifilm's Exif data uses Motorola align, but MakerNote ignores it and uses Intel
     
    3745 *   of TIFF header (same as the other IFD), but Fujifilm counts it from the first byte
    3846 *   of MakerNote itself.
     47 *
     48 * @author Drew Noakes http://drewnoakes.com
    3949 */
    40 public class FujifilmMakernoteDescriptor extends TagDescriptor
     50public class FujifilmMakernoteDescriptor extends TagDescriptor<FujifilmMakernoteDirectory>
    4151{
    42     public FujifilmMakernoteDescriptor(Directory directory)
     52    public FujifilmMakernoteDescriptor(@NotNull FujifilmMakernoteDirectory directory)
    4353    {
    4454        super(directory);
    4555    }
    4656
    47     public String getDescription(int tagType) throws MetadataException
     57    @Nullable
     58    public String getDescription(int tagType)
    4859    {
    4960        switch (tagType) {
     
    5263            case FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE:
    5364                return getWhiteBalanceDescription();
    54             case FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR:
     65            case FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION:
    5566                return getColorDescription();
    5667            case FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE:
     
    6475            case FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE:
    6576                return getFocusModeDescription();
    66             case FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO:
     77            case FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH:
    6778                return getSlowSyncDescription();
    6879            case FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE:
     
    7788                return getAutoExposureWarningDescription();
    7889            default:
    79                 return _directory.getString(tagType);
    80         }
    81     }
    82 
    83     public String getAutoExposureWarningDescription() throws MetadataException
    84     {
    85         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING)) return null;
    86         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING);
    87         switch (value) {
    88             case 0:
    89                 return "AE good";
    90             case 1:
    91                 return "Over exposed (>1/1000s @ F11)";
    92             default:
    93                 return "Unknown (" + value + ")";
    94         }
    95     }
    96 
    97     public String getFocusWarningDescription() throws MetadataException
    98     {
    99         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING)) return null;
    100         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING);
    101         switch (value) {
    102             case 0:
    103                 return "Auto focus good";
    104             case 1:
    105                 return "Out of focus";
    106             default:
    107                 return "Unknown (" + value + ")";
    108         }
    109     }
    110 
    111     public String getBlurWarningDescription() throws MetadataException
    112     {
    113         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING)) return null;
    114         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING);
    115         switch (value) {
    116             case 0:
    117                 return "No blur warning";
    118             case 1:
    119                 return "Blur warning";
    120             default:
    121                 return "Unknown (" + value + ")";
    122         }
    123     }
    124 
    125     public String getContinuousTakingOrAutoBrackettingDescription() throws MetadataException
    126     {
    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);
    129         switch (value) {
    130             case 0:
     90                return super.getDescription(tagType);
     91        }
     92    }
     93
     94    @Nullable
     95    public String getSharpnessDescription()
     96    {
     97        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS);
     98        if (value==null)
     99            return null;
     100        switch (value) {
     101            case 1:
     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";
     111            default:
     112                return "Unknown (" + value + ")";
     113        }
     114    }
     115
     116    @Nullable
     117    public String getWhiteBalanceDescription()
     118    {
     119        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE);
     120        if (value==null)
     121            return null;
     122        switch (value) {
     123            case 0:
     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";
     139            default:
     140                return "Unknown (" + value + ")";
     141        }
     142    }
     143
     144    @Nullable
     145    public String getColorDescription()
     146    {
     147        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION);
     148        if (value==null)
     149            return null;
     150        switch (value) {
     151            case 0:
     152                return "Normal (STD)";
     153            case 256:
     154                return "High (HARD)";
     155            case 512:
     156                return "Low (ORG)";
     157            default:
     158                return "Unknown (" + value + ")";
     159        }
     160    }
     161
     162    @Nullable
     163    public String getToneDescription()
     164    {
     165        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE);
     166        if (value==null)
     167            return null;
     168        switch (value) {
     169            case 0:
     170                return "Normal (STD)";
     171            case 256:
     172                return "High (HARD)";
     173            case 512:
     174                return "Low (ORG)";
     175            default:
     176                return "Unknown (" + value + ")";
     177        }
     178    }
     179
     180    @Nullable
     181    public String getFlashModeDescription()
     182    {
     183        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE);
     184        if (value==null)
     185            return null;
     186        switch (value) {
     187            case 0:
     188                return "Auto";
     189            case 1:
     190                return "On";
     191            case 2:
    131192                return "Off";
    132             case 1:
    133                 return "On";
    134             default:
    135                 return "Unknown (" + value + ")";
    136         }
    137     }
    138 
    139     public String getPictureModeDescription() throws MetadataException
    140     {
    141         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE)) return null;
    142         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE);
     193            case 3:
     194                return "Red-eye reduction";
     195            default:
     196                return "Unknown (" + value + ")";
     197        }
     198    }
     199
     200    @Nullable
     201    public String getFlashStrengthDescription()
     202    {
     203        Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH);
     204        if (value==null)
     205            return null;
     206        return value.toSimpleString(false) + " EV (Apex)";
     207    }
     208
     209    @Nullable
     210    public String getMacroDescription()
     211    {
     212        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO);
     213    }
     214
     215    @Nullable
     216    public String getFocusModeDescription()
     217    {
     218        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE);
     219        if (value==null)
     220            return null;
     221        switch (value) {
     222            case 0:
     223                return "Auto focus";
     224            case 1:
     225                return "Manual focus";
     226            default:
     227                return "Unknown (" + value + ")";
     228        }
     229    }
     230
     231    @Nullable
     232    public String getSlowSyncDescription()
     233    {
     234        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH);
     235    }
     236
     237    @Nullable
     238    public String getPictureModeDescription()
     239    {
     240        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE);
     241        if (value==null)
     242            return null;
    143243        switch (value) {
    144244            case 0:
     
    165265    }
    166266
    167     public String getSlowSyncDescription() throws MetadataException
    168     {
    169         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO)) return null;
    170         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO);
     267    @Nullable
     268    public String getContinuousTakingOrAutoBrackettingDescription()
     269    {
     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;
     279        switch (value) {
     280            case 0:
     281                return "No blur warning";
     282            case 1:
     283                return "Blur warning";
     284            default:
     285                return "Unknown (" + value + ")";
     286        }
     287    }
     288
     289    @Nullable
     290    public String getFocusWarningDescription()
     291    {
     292        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING);
     293        if (value==null)
     294            return null;
     295        switch (value) {
     296            case 0:
     297                return "Auto focus good";
     298            case 1:
     299                return "Out of focus";
     300            default:
     301                return "Unknown (" + value + ")";
     302        }
     303    }
     304
     305    @Nullable
     306    public String getAutoExposureWarningDescription()
     307    {
     308        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING);
     309        if (value==null)
     310            return null;
     311        switch (value) {
     312            case 0:
     313                return "AE good";
     314            case 1:
     315                return "Over exposed (>1/1000s @ F11)";
     316            default:
     317                return "Unknown (" + value + ")";
     318        }
     319    }
     320
     321
     322    @Nullable
     323    private String getOnOffDescription(final int tagType)
     324    {
     325        final Integer value = _directory.getInteger(tagType);
     326        if (value==null)
     327            return null;
    171328        switch (value) {
    172329            case 0:
     
    178335        }
    179336    }
    180 
    181     public String getFocusModeDescription() throws MetadataException
    182     {
    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         }
    193     }
    194 
    195     public String getMacroDescription() throws MetadataException
    196     {
    197         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO)) return null;
    198         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO);
    199         switch (value) {
    200             case 0:
    201                 return "Off";
    202             case 1:
    203                 return "On";
    204             default:
    205                 return "Unknown (" + value + ")";
    206         }
    207     }
    208 
    209     public String getFlashStrengthDescription() throws MetadataException
    210     {
    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)";
    214     }
    215 
    216     public String getFlashModeDescription() throws MetadataException
    217     {
    218         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE)) return null;
    219         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE);
    220         switch (value) {
    221             case 0:
    222                 return "Auto";
    223             case 1:
    224                 return "On";
    225             case 2:
    226                 return "Off";
    227             case 3:
    228                 return "Red-eye reduction";
    229             default:
    230                 return "Unknown (" + value + ")";
    231         }
    232     }
    233 
    234     public String getToneDescription() throws MetadataException
    235     {
    236         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE)) return null;
    237         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE);
    238         switch (value) {
    239             case 0:
    240                 return "Normal (STD)";
    241             case 256:
    242                 return "High (HARD)";
    243             case 512:
    244                 return "Low (ORG)";
    245             default:
    246                 return "Unknown (" + value + ")";
    247         }
    248     }
    249 
    250     public String getColorDescription() throws MetadataException
    251     {
    252         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR)) return null;
    253         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR);
    254         switch (value) {
    255             case 0:
    256                 return "Normal (STD)";
    257             case 256:
    258                 return "High";
    259             case 512:
    260                 return "Low (ORG)";
    261             default:
    262                 return "Unknown (" + value + ")";
    263         }
    264     }
    265 
    266     public String getWhiteBalanceDescription() throws MetadataException
    267     {
    268         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE)) return null;
    269         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE);
    270         switch (value) {
    271             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";
    287             default:
    288                 return "Unknown (" + value + ")";
    289         }
    290     }
    291 
    292     public String getSharpnessDescription() throws MetadataException
    293     {
    294         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS)) return null;
    295         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS);
    296         switch (value) {
    297             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";
    307             default:
    308                 return "Unknown (" + value + ")";
    309         }
    310     }
    311337}
  • trunk/src/com/drew/metadata/exif/FujifilmMakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Fujifilm cameras.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class FujifilmMakernoteDirectory extends Directory
    2734{
    2835    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_SYNCHRO = 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
    4653
    47     protected static final HashMap tagNameMap = new HashMap();
     54    @NotNull
     55    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    4856
    4957    static
    5058    {
    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), "Focus Mode");
    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");
    6977    }
    7078
     
    7482    }
    7583
     84    @NotNull
    7685    public String getName()
    7786    {
     
    7988    }
    8089
    81     protected HashMap getTagNameMap()
     90    @NotNull
     91    protected HashMap<Integer, String> getTagNameMap()
    8292    {
    83         return tagNameMap;
     93        return _tagNameMap;
    8494    }
    8595}
  • trunk/src/com/drew/metadata/exif/GpsDescriptor.java

    r4231 r6127  
    11/*
    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 12-Nov-2002 22:27:52 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.GeoLocation;
    1924import com.drew.lang.Rational;
    20 import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
    2227import com.drew.metadata.TagDescriptor;
    2328
     29import java.text.DecimalFormat;
     30
    2431/**
    25  *
     32 * Provides human-readable string representations of tag values stored in a <code>GpsDirectory</code>.
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2635 */
    27 public class GpsDescriptor extends TagDescriptor
     36public class GpsDescriptor extends TagDescriptor<GpsDirectory>
    2837{
    29     public GpsDescriptor(Directory directory)
     38    public GpsDescriptor(@NotNull GpsDirectory directory)
    3039    {
    3140        super(directory);
    3241    }
    3342
    34     public String getDescription(int tagType) throws MetadataException
     43    @Nullable
     44    public String getDescription(int tagType)
    3545    {
    3646        switch (tagType) {
     47            case GpsDirectory.TAG_GPS_VERSION_ID:
     48                return getGpsVersionIdDescription();
    3749            case GpsDirectory.TAG_GPS_ALTITUDE:
    3850                return getGpsAltitudeDescription();
     
    5769            case GpsDirectory.TAG_GPS_TIME_STAMP:
    5870                return getGpsTimeStampDescription();
     71            case GpsDirectory.TAG_GPS_LONGITUDE:
    5972                // three rational numbers -- displayed in HH"MM"SS.ss
    60             case GpsDirectory.TAG_GPS_LONGITUDE:
    6173                return getGpsLongitudeDescription();
    6274            case GpsDirectory.TAG_GPS_LATITUDE:
     75                // three rational numbers -- displayed in HH"MM"SS.ss
    6376                return getGpsLatitudeDescription();
     77            case GpsDirectory.TAG_GPS_DIFFERENTIAL:
     78                return getGpsDifferentialDescription();
    6479            default:
    65                 return _directory.getString(tagType);
    66         }
    67     }
    68 
    69     public String getGpsLatitudeDescription() throws MetadataException
    70     {
    71         if (!_directory.containsTag(GpsDirectory.TAG_GPS_LATITUDE)) return null;
    72         return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LATITUDE);
    73     }
    74 
    75     public String getGpsLongitudeDescription() throws MetadataException
    76     {
    77         if (!_directory.containsTag(GpsDirectory.TAG_GPS_LONGITUDE)) return null;
    78         return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LONGITUDE);
    79     }
    80 
    81     public String getHoursMinutesSecondsDescription(int tagType) throws MetadataException
    82     {
    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);
    91     }
    92 
    93     public String getGpsTimeStampDescription() throws MetadataException
     80                return super.getDescription(tagType);
     81        }
     82    }
     83
     84    @Nullable
     85    private String getGpsVersionIdDescription()
     86    {
     87        return convertBytesToVersionString(_directory.getIntArray(GpsDirectory.TAG_GPS_VERSION_ID), 1);
     88    }
     89
     90    @Nullable
     91    public String getGpsLatitudeDescription()
     92    {
     93        GeoLocation location = _directory.getGeoLocation();
     94
     95        if (location == null)
     96            return null;
     97
     98        return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude());
     99    }
     100
     101    @Nullable
     102    public String getGpsLongitudeDescription()
     103    {
     104        GeoLocation location = _directory.getGeoLocation();
     105
     106        if (location == null)
     107            return null;
     108
     109        return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude());
     110    }
     111
     112    @Nullable
     113    public String getGpsTimeStampDescription()
    94114    {
    95115        // time in hour, min, sec
    96         if (!_directory.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP)) return null;
    97116        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();
    106     }
    107 
     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();
     127    }
     128
     129    @Nullable
    108130    public String getGpsDestinationReferenceDescription()
    109131    {
    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)) {
    113137            return "kilometers";
    114         } else if ("M".equalsIgnoreCase(destRef)) {
     138        } else if ("M".equalsIgnoreCase(distanceRef)) {
    115139            return "miles";
    116         } else if ("N".equalsIgnoreCase(destRef)) {
     140        } else if ("N".equalsIgnoreCase(distanceRef)) {
    117141            return "knots";
    118142        } else {
    119             return "Unknown (" + destRef + ")";
    120         }
    121     }
    122 
     143            return "Unknown (" + distanceRef + ")";
     144        }
     145    }
     146
     147    @Nullable
    123148    public String getGpsDirectionDescription(int tagType)
    124149    {
    125         if (!_directory.containsTag(tagType)) return null;
    126         String gpsDirection = _directory.getString(tagType).trim();
    127         return gpsDirection + " degrees";
    128     }
    129 
     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";
     158    }
     159
     160    @Nullable
    130161    public String getGpsDirectionReferenceDescription(int tagType)
    131162    {
    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();
    134167        if ("T".equalsIgnoreCase(gpsDistRef)) {
    135168            return "True direction";
     
    141174    }
    142175
     176    @Nullable
    143177    public String getGpsSpeedRefDescription()
    144178    {
    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();
    147183        if ("K".equalsIgnoreCase(gpsSpeedRef)) {
    148184            return "kph";
     
    156192    }
    157193
     194    @Nullable
    158195    public String getGpsMeasureModeDescription()
    159196    {
    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();
    162201        if ("2".equalsIgnoreCase(gpsSpeedMeasureMode)) {
    163202            return "2-dimensional measurement";
     
    169208    }
    170209
     210    @Nullable
    171211    public String getGpsStatusDescription()
    172212    {
    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();
    175217        if ("A".equalsIgnoreCase(gpsStatus)) {
    176             return "Measurement in progess";
     218            return "Active (Measurement in progress)";
    177219        } else if ("V".equalsIgnoreCase(gpsStatus)) {
    178             return "Measurement Interoperability";
     220            return "Void (Measurement Interoperability)";
    179221        } else {
    180222            return "Unknown (" + gpsStatus + ")";
     
    182224    }
    183225
    184     public String getGpsAltitudeRefDescription() throws MetadataException
    185     {
    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) {
     226    @Nullable
     227    public String getGpsAltitudeRefDescription()
     228    {
     229        Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_ALTITUDE_REF);
     230        if (value==null)
     231            return null;
     232        if (value == 0)
    189233            return "Sea level";
    190         } else {
    191             return "Unknown (" + alititudeRef + ")";
    192         }
    193     }
    194 
    195     public String getGpsAltitudeDescription() throws MetadataException
    196     {
    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";
     234        if (value == 1)
     235            return "Below sea level";
     236        return "Unknown (" + value + ")";
     237    }
     238
     239    @Nullable
     240    public String getGpsAltitudeDescription()
     241    {
     242        final Rational value = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE);
     243        if (value==null)
     244            return null;
     245        return value.intValue() + " metres";
     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();
    200270    }
    201271}
  • trunk/src/com/drew/metadata/exif/GpsDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.GeoLocation;
     24import com.drew.lang.Rational;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
    1927import com.drew.metadata.Directory;
    2028
     
    2230
    2331/**
     32 * Describes Exif tags that contain Global Positioning System (GPS) data.
    2433 *
     34 * @author Drew Noakes http://drewnoakes.com
    2535 */
    2636public class GpsDirectory extends Directory
     
    8191    public static final int TAG_GPS_DEST_DISTANCE = 0x001A;
    8292
    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;
     98
     99    @NotNull
     100    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    84101
    85102    static
    86103    {
    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");
    114135    }
    115136
     
    119140    }
    120141
     142    @NotNull
    121143    public String getName()
    122144    {
     
    124146    }
    125147
    126     protected HashMap getTagNameMap()
     148    @NotNull
     149    protected HashMap<Integer, String> getTagNameMap()
    127150    {
    128         return tagNameMap;
     151        return _tagNameMap;
     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);
    129184    }
    130185}
  • trunk/src/com/drew/metadata/exif/KodakMakernoteDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.TagDescriptor;
    2025
    2126/**
    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 *
    2329 * Thanks to David Carson for the initial version of this class.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
    2432 */
    25 public class KodakMakernoteDescriptor extends TagDescriptor
     33public class KodakMakernoteDescriptor extends TagDescriptor<KodakMakernoteDirectory>
    2634{
    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)
    3336    {
    34                 return _directory.getString(tagType);
    35         }
     37        super(directory);
     38    }
    3639}
  • trunk/src/com/drew/metadata/exif/KodakMakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
     23import com.drew.lang.annotations.NotNull;
    1724import com.drew.metadata.Directory;
    1825
     
    2128/**
    2229 * Describes tags specific to Kodak cameras.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
    2332 */
    2433public class KodakMakernoteDirectory extends Directory
    2534{
    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()
    2939    {
    30                 return "Kodak Makernote";
    31         }
     40        this.setDescriptor(new KodakMakernoteDescriptor(this));
     41    }
    3242
    33         protected HashMap getTagNameMap()
     43    @NotNull
     44    public String getName()
    3445    {
    35                 return _tagNameMap;
    36         }
     46        return "Kodak Makernote";
     47    }
     48
     49    @NotNull
     50    protected HashMap<Integer, String> getTagNameMap()
     51    {
     52        return _tagNameMap;
     53    }
    3754}
  • trunk/src/com/drew/metadata/exif/KyoceraMakernoteDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
    2127/**
    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/>
    2430 * Some information about this makernote taken from here:
    2531 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
    26  *
     32 * <p/>
    2733 * Most manufacturer's MakerNote counts the "offset to data" from the first byte
    2834 * of TIFF header (same as the other IFD), but Kyocera (along with Fujifilm) counts
    2935 * it from the first byte of MakerNote itself.
     36 *
     37 * @author Drew Noakes http://drewnoakes.com
    3038 */
    31 public class KyoceraMakernoteDescriptor extends TagDescriptor
     39public class KyoceraMakernoteDescriptor extends TagDescriptor<KyoceraMakernoteDirectory>
    3240{
    33     public KyoceraMakernoteDescriptor(Directory directory)
     41    public KyoceraMakernoteDescriptor(@NotNull KyoceraMakernoteDirectory directory)
    3442    {
    3543        super(directory);
    3644    }
    3745
    38     public String getDescription(int tagType) throws MetadataException
     46    @Nullable
     47    public String getDescription(int tagType)
    3948    {
    4049        switch (tagType) {
     
    4453                return getProprietaryThumbnailDataDescription();
    4554            default:
    46                 return _directory.getString(tagType);
     55                return super.getDescription(tagType);
    4756        }
    4857    }
    4958
    50     public String getPrintImageMatchingInfoDescription() throws MetadataException
     59    @Nullable
     60    public String getPrintImageMatchingInfoDescription()
    5161    {
    52         if (!_directory.containsTag(KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO)) return null;
    5362        byte[] bytes = _directory.getByteArray(KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO);
     63        if (bytes==null)
     64            return null;
    5465        return "(" + bytes.length + " bytes)";
    5566    }
    5667
    57     public String getProprietaryThumbnailDataDescription() throws MetadataException
     68    @Nullable
     69    public String getProprietaryThumbnailDataDescription()
    5870    {
    59         if (!_directory.containsTag(KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL)) return null;
    6071        byte[] bytes = _directory.getByteArray(KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL);
     72        if (bytes==null)
     73            return null;
    6174        return "(" + bytes.length + " bytes)";
    6275    }
  • trunk/src/com/drew/metadata/exif/KyoceraMakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags specific to Kyocera and Contax cameras.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class KyoceraMakernoteDirectory extends Directory
     
    2936    public static final int TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    3037
    31     protected static final HashMap tagNameMap = new HashMap();
     38    @NotNull
     39    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    3240
    3341    static
    3442    {
    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");
    3745    }
    3846
     
    4250    }
    4351
     52    @NotNull
    4453    public String getName()
    4554    {
     
    4756    }
    4857
    49     protected HashMap getTagNameMap()
     58    @NotNull
     59    protected HashMap<Integer, String> getTagNameMap()
    5060    {
    51         return tagNameMap;
     61        return _tagNameMap;
    5262    }
    5363}
  • trunk/src/com/drew/metadata/exif/NikonType1MakernoteDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    1723import com.drew.lang.Rational;
    18 import com.drew.metadata.Directory;
    19 import com.drew.metadata.MetadataException;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
    2026import com.drew.metadata.TagDescriptor;
    2127
    2228/**
    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/>
    2431 * Type-1 is for E-Series cameras prior to (not including) E990.  For example: E700, E800, E900,
    2532 * E900S, E910, E950.
    26  *
     33 * <p/>
    2734 * MakerNote starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from
    2835 * offset 0x08. This is the same as Olympus except start string. Example of actual data
     
    3239 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
    3340 * </code></pre>
     41 *
     42 * @author Drew Noakes http://drewnoakes.com
    3443 */
    35 public class NikonType1MakernoteDescriptor extends TagDescriptor
     44public class NikonType1MakernoteDescriptor extends TagDescriptor<NikonType1MakernoteDirectory>
    3645{
    37     public NikonType1MakernoteDescriptor(Directory directory)
     46    public NikonType1MakernoteDescriptor(@NotNull NikonType1MakernoteDirectory directory)
    3847    {
    3948        super(directory);
    4049    }
    4150
    42     public String getDescription(int tagType) throws MetadataException
     51    @Nullable
     52    public String getDescription(int tagType)
    4353    {
    4454        switch (tagType) {
     
    6070                return getConverterDescription();
    6171            default:
    62                 return _directory.getString(tagType);
    63         }
    64     }
    65 
    66     public String getConverterDescription() throws MetadataException
    67     {
    68         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER)) return null;
    69         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER);
     72                return super.getDescription(tagType);
     73        }
     74    }
     75
     76    @Nullable
     77    public String getConverterDescription()
     78    {
     79        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER);
     80        if (value == null)
     81            return null;
    7082        switch (value) {
    7183            case 0:
     
    7890    }
    7991
    80     public String getDigitalZoomDescription() throws MetadataException
    81     {
    82         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM)) return null;
     92    @Nullable
     93    public String getDigitalZoomDescription()
     94    {
    8395        Rational value = _directory.getRational(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM);
     96        if (value == null)
     97            return null;
    8498        if (value.getNumerator() == 0) {
    8599            return "No digital zoom";
     
    88102    }
    89103
    90     public String getFocusDescription() throws MetadataException
    91     {
    92         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS)) return null;
     104    @Nullable
     105    public String getFocusDescription()
     106    {
    93107        Rational value = _directory.getRational(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS);
     108        if (value == null)
     109            return null;
    94110        if (value.getNumerator() == 1 && value.getDenominator() == 0) {
    95111            return "Infinite";
     
    98114    }
    99115
    100     public String getWhiteBalanceDescription() throws MetadataException
    101     {
    102         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE)) return null;
    103         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE);
     116    @Nullable
     117    public String getWhiteBalanceDescription()
     118    {
     119        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE);
     120        if (value == null)
     121            return null;
    104122        switch (value) {
    105123            case 0:
     
    110128                return "Daylight";
    111129            case 3:
    112                 return "Incandescense";
    113             case 4:
    114                 return "Flourescence";
     130                return "Incandescence";
     131            case 4:
     132                return "Florescence";
    115133            case 5:
    116134                return "Cloudy";
     
    122140    }
    123141
    124     public String getCcdSensitivityDescription() throws MetadataException
    125     {
    126         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY)) return null;
    127         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY);
     142    @Nullable
     143    public String getCcdSensitivityDescription()
     144    {
     145        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY);
     146        if (value == null)
     147            return null;
    128148        switch (value) {
    129149            case 0:
     
    140160    }
    141161
    142     public String getImageAdjustmentDescription() throws MetadataException
    143     {
    144         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT)) return null;
    145         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT);
     162    @Nullable
     163    public String getImageAdjustmentDescription()
     164    {
     165        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT);
     166        if (value == null)
     167            return null;
    146168        switch (value) {
    147169            case 0:
     
    160182    }
    161183
    162     public String getColorModeDescription() throws MetadataException
    163     {
    164         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE)) return null;
    165         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE);
     184    @Nullable
     185    public String getColorModeDescription()
     186    {
     187        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE);
     188        if (value == null)
     189            return null;
    166190        switch (value) {
    167191            case 1:
     
    174198    }
    175199
    176     public String getQualityDescription() throws MetadataException
    177     {
    178         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY)) return null;
    179         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY);
     200    @Nullable
     201    public String getQualityDescription()
     202    {
     203        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY);
     204        if (value == null)
     205            return null;
    180206        switch (value) {
    181207            case 1:
  • trunk/src/com/drew/metadata/exif/NikonType1MakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
    24  * Contains values specific to Nikon cameras.  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.
    2530 *
    2631 * There are 3 formats of Nikon's MakerNote. MakerNote of E700/E800/E900/E900S/E910/E950
     
    3237 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
    3338 * </code></pre>
     39 *
     40 * @author Drew Noakes http://drewnoakes.com
    3441 */
    3542public class NikonType1MakernoteDirectory extends Directory
     
    4754    public static final int TAG_NIKON_TYPE1_UNKNOWN_3 = 0x0F00;
    4855
    49     protected static final HashMap _tagNameMap = new HashMap();
     56    @NotNull
     57    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    5058
    5159    static
    5260    {
    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");
    6472    }
    6573
     
    6977    }
    7078
     79    @NotNull
    7180    public String getName()
    7281    {
     
    7483    }
    7584
    76     protected HashMap getTagNameMap()
     85    @NotNull
     86    protected HashMap<Integer, String> getTagNameMap()
    7787    {
    7888        return _tagNameMap;
  • trunk/src/com/drew/metadata/exif/NikonType2MakernoteDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    1723import com.drew.lang.Rational;
    18 import com.drew.metadata.Directory;
    19 import com.drew.metadata.MetadataException;
     24import com.drew.lang.StringUtil;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
    2027import com.drew.metadata.TagDescriptor;
    2128
    2229import java.text.DecimalFormat;
     30import java.util.ArrayList;
     31import java.util.Collection;
     32import java.util.Date;
    2333
    2434/**
    25  * Provides human-readable string versions of the tags stored in a NikonType2MakernoteDirectory.
     35 * Provides human-readable string representations of tag values stored in a <code>NikonType2MakernoteDirectory</code>.
     36 *
    2637 * 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
    2740 */
    28 public class NikonType2MakernoteDescriptor extends TagDescriptor
     41public class NikonType2MakernoteDescriptor extends TagDescriptor<NikonType2MakernoteDirectory>
    2942{
    30     public NikonType2MakernoteDescriptor(Directory directory)
     43    public NikonType2MakernoteDescriptor(@NotNull NikonType2MakernoteDirectory directory)
    3144    {
    3245        super(directory);
    3346    }
    3447
    35     private NikonType2MakernoteDirectory getMakernoteDirectory()
    36     {
    37         return (NikonType2MakernoteDirectory)_directory;
    38     }
    39 
    40     public String getDescription(int tagType) throws MetadataException
     48    @Nullable
     49    public String getDescription(int tagType)
    4150    {
    4251        switch (tagType)
    4352        {
     53            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT:
     54                return getProgramShiftDescription();
     55            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE:
     56                return getExposureDifferenceDescription();
    4457            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS:
    4558                return getLensDescription();
     
    5063            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION:
    5164                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();
    5279            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1:
    5380                return getIsoSettingDescription();
    5481            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM:
    5582                return getDigitalZoomDescription();
     83            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED:
     84                return getFlashUsedDescription();
    5685            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION:
    5786                return getAutoFocusPositionDescription();
    5887            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION:
    59                 return getAutoFirmwareVersionDescription();
     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();
    6099            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;
     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    {
    68267        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION);
     268        if (values==null)
     269            return null;
    69270        if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) {
    70271            return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION) + ")";
     
    86287    }
    87288
    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;
     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    {
    101364        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1);
    102         if (values[0] != 0 || values[1] == 0) {
     365        if (values==null)
     366            return null;
     367        if (values[0] != 0 || values[1] == 0)
    103368            return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1) + ")";
    104         }
    105369        return "ISO " + values[1];
    106370    }
    107371
    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)
     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)
    127381            return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
    128382
    129         StringBuffer description = new StringBuffer();
    130         description.append(lensValues[0].intValue());
     383        StringBuilder description = new StringBuilder();
     384        description.append(values[0].intValue());
    131385        description.append('-');
    132         description.append(lensValues[1].intValue());
     386        description.append(values[1].intValue());
    133387        description.append("mm f/");
    134         description.append(lensValues[2].floatValue());
     388        description.append(values[2].floatValue());
    135389        description.append('-');
    136         description.append(lensValues[3].floatValue());
     390        description.append(values[3].floatValue());
    137391
    138392        return description.toString();
    139393    }
    140394
     395    @Nullable
    141396    public String getHueAdjustmentDescription()
    142397    {
    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 
     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
    149405    public String getColorModeDescription()
    150406    {
    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"))
     407        String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE);
     408        if (value==null)
     409            return null;
     410        if (value.startsWith("MODE1"))
    156411            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);
     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);
    168422    }
    169423}
  • trunk/src/com/drew/metadata/exif/NikonType2MakernoteDirectory.java

    r4258 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    149 *
    15  * Created by dnoakes on 3-Oct-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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
    19 import com.drew.lang.Rational;
     23import com.drew.lang.annotations.NotNull;
    2024import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
    2225
    2326import java.util.HashMap;
     
    2629 * Describes tags specific to Nikon (type 2) cameras.  Type-2 applies to the E990 and D-series cameras such as the E990, D1,
    2730 * D70 and D100.
    28  *
     31 * <p/>
    2932 * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data.
    3033 * http://www.timelesswanderings.net/equipment/D100/NEF.html
    31  *
     34 * <p/>
    3235 * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute.  Similarly
    3336 * image hiding uses the 'hidden' attribute (observed on the D70).  Consequently, these values are not available here.
    34  *
     37 * <p/>
    3538 * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field.
    3639 * New tags have subsequently been added since Fabrizio's observations.
    37  *
     40 * <p/>
    3841 * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD.  In
    3942 * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also
    4043 * provide this prefix to aid in software decoding).
     44 *
     45 * @author Drew Noakes http://drewnoakes.com
    4146 */
    4247public class NikonType2MakernoteDirectory extends Directory
     
    5762
    5863    /**
    59      * Values observed
    60      * - COLOR (seen in the D1X)
     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>
    6172     */
    6273    public static final int TAG_NIKON_TYPE2_COLOR_MODE = 0x0003;
    6374
    6475    /**
    65      * Values observed
    66      * - FILE
    67      * - RAW
    68      * - NORMAL
    69      * - FINE
     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>
    7084     */
    7185    public static final int TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT = 0x0004;
    7286
    7387    /**
    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)
     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>
    81107     */
    82108    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE  = 0x0005;
    83109
    84110    /**
    85      * The sharpening as set in the camera.
    86      *
    87      * Values observed
    88      * - AUTO
    89      * - NORMAL (D70)
    90      * - NONE (D1X)
     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>
    91122     */
    92123    public static final int TAG_NIKON_TYPE2_CAMERA_SHARPENING = 0x0006;
    93124
    94125    /**
    95      * The auto-focus type used by the camera.
    96      *
    97      * Values observed
    98      * - AF-S
    99      * - AF-C
    100      * - MANUAL
     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>
    101134     */
    102135    public static final int TAG_NIKON_TYPE2_AF_TYPE = 0x0007;
    103136
    104137    /**
    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"
     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"
    110150     */
    111151    public static final int TAG_NIKON_TYPE2_FLASH_SYNC_MODE = 0x0008;
    112152
    113153    /**
    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)
     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>
    119163     */
    120164    public static final int TAG_NIKON_TYPE2_AUTO_FLASH_MODE = 0x0009;
    121165
    122166    /**
    123      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     167     * An unknown tag, as a rational.  Several values given here:
     168     * http://gvsoft.homedns.org/exif/makernote-nikon-type2.html#0x000b
    124169     */
    125170    public static final int TAG_NIKON_TYPE2_UNKNOWN_34 = 0x000A;
    126171
    127172    /**
    128      * Values observed
    129      * - 0
     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>
    130185     */
    131186    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE = 0x000B;
     
    143198
    144199    /**
    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;
     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;
    155234
    156235    /**
    157236     * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    158237     */
    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      */
    164238    public static final int TAG_NIKON_TYPE2_DATA_DUMP = 0x0010;
    165239
    166240    /**
    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)
     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>
    178257     */
    179258    public static final int TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION = 0x0012;
    180259
    181260    /**
    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
     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)
    190274     * - 0 0 49163 53255
    191275     * - 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.
     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>
    215408     */
    216409    public static final int TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT = 0x0080;
    217410
    218411    /**
    219      * The tone compensation as set in the camera.
    220      *
    221      * Values observed
    222      * - AUTO
    223      * - NORMAL (D1X, D100)
     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>
    224422     */
    225423    public static final int TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION = 0x0081;
    226424
    227425    /**
    228      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     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>
    229435     */
    230436    public static final int TAG_NIKON_TYPE2_ADAPTER = 0x0082;
    231437
    232438    /**
    233      * Values observed
    234      * - 6
    235      * - 6 (D70)
    236      * - 2 (D1X)
    237      */
    238     public static final int TAG_NIKON_TYPE2_UNKNOWN_4 = 0x0083;
     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;
    239452
    240453    /**
     
    260473
    261474    /**
    262      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     475     * The amount of digital zoom used.
    263476     */
    264477    public static final int TAG_NIKON_TYPE2_DIGITAL_ZOOM = 0x0086;
    265478
    266479    /**
     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     *
    267767     * 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();
     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>();
    448781
    449782    static
    450783    {
    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");
     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");
    507904    }
    508905
     
    512909    }
    513910
    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 
     911    @NotNull
    534912    public String getName()
    535913    {
     
    537915    }
    538916
    539     protected HashMap getTagNameMap()
     917    @NotNull
     918    protected HashMap<Integer, String> getTagNameMap()
    540919    {
    541920        return _tagNameMap;
  • trunk/src/com/drew/metadata/exif/OlympusMakernoteDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
    2127/**
    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
    2331 */
    24 public class OlympusMakernoteDescriptor extends TagDescriptor
     32public class OlympusMakernoteDescriptor extends TagDescriptor<OlympusMakernoteDirectory>
    2533{
    26     public OlympusMakernoteDescriptor(Directory directory)
     34    public OlympusMakernoteDescriptor(@NotNull OlympusMakernoteDirectory directory)
    2735    {
    2836        super(directory);
    2937    }
    3038
    31     public String getDescription(int tagType) throws MetadataException
     39    @Nullable
     40    public String getDescription(int tagType)
    3241    {
    3342        switch (tagType) {
     
    4150                return getDigiZoomRatioDescription();
    4251            default:
    43                 return _directory.getString(tagType);
     52                return super.getDescription(tagType);
    4453        }
    4554    }
    4655
    47     public String getDigiZoomRatioDescription() throws MetadataException
     56    @Nullable
     57    public String getDigiZoomRatioDescription()
    4858    {
    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;
    5162        switch (value) {
    5263            case 0:
     
    5970    }
    6071
    61     public String getMacroModeDescription() throws MetadataException
     72    @Nullable
     73    public String getMacroModeDescription()
    6274    {
    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;
    6578        switch (value) {
    6679            case 0:
     
    7386    }
    7487
    75     public String getJpegQualityDescription() throws MetadataException
     88    @Nullable
     89    public String getJpegQualityDescription()
    7690    {
    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;
    7994        switch (value) {
    8095            case 1:
     
    89104    }
    90105
    91     public String getSpecialModeDescription() throws MetadataException
     106    @Nullable
     107    public String getSpecialModeDescription()
    92108    {
    93         if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE)) return null;
    94109        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();
    96115        switch (values[0]) {
    97116            case 0:
     
    111130                break;
    112131        }
     132
     133        if (values.length < 2)
     134            return desc.toString();
    113135        desc.append(" - ");
    114136        switch (values[1]) {
     
    117139                break;
    118140            case 1:
    119                 desc.append("1st in a sequnce");
     141                desc.append("1st in a sequence");
    120142                break;
    121143            case 2:
     
    130152                break;
    131153        }
     154        if (values.length < 3)
     155            return desc.toString();
     156        desc.append(" - ");
    132157        switch (values[2]) {
    133158            case 1:
  • trunk/src/com/drew/metadata/exif/OlympusMakernoteDirectory.java

    r4231 r6127  
    11/*
    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 27-Nov-2002 10:10:47 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
    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
    2633 */
    2734public class OlympusMakernoteDirectory extends Directory
    2835{
    29     /**
    30      * Used by Konica / Minolta cameras.
    31      */
     36    /** Used by Konica / Minolta cameras. */
    3237    public static final int TAG_OLYMPUS_MAKERNOTE_VERSION = 0x0000;
    33 
    34     /**
    35      * Used by Konica / Minolta cameras.
    36      */
     38    /** Used by Konica / Minolta cameras. */
    3739    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. */
    4241    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. */
    4743    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. */
    5245    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. */
    5747    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. */
    6249    public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH = 0x0089;
    6350
     
    8471    /**
    8572     * Not 100% sure about this tag.
    86      *
     73     * <p/>
    8774     * Used by Konica / Minolta cameras.
    8875     * 0 = Raw
     
    119106    public static final int TAG_OLYMPUS_MACRO_MODE = 0x0202;
    120107
    121     /**
    122      *
    123      */
    124108    public static final int TAG_OLYMPUS_UNKNOWN_1 = 0x0203;
    125109
    126     /**
    127      * Zoom Factor (0 or 1 = normal)
    128      */
     110    /** Zoom Factor (0 or 1 = normal) */
    129111    public static final int TAG_OLYMPUS_DIGI_ZOOM_RATIO = 0x0204;
    130 
    131     /**
    132      *
    133      */
    134112    public static final int TAG_OLYMPUS_UNKNOWN_2 = 0x0205;
    135 
    136     /**
    137      *
    138      */
    139113    public static final int TAG_OLYMPUS_UNKNOWN_3 = 0x0206;
    140 
    141     /**
    142      *
    143      */
    144114    public static final int TAG_OLYMPUS_FIRMWARE_VERSION = 0x0207;
    145 
    146     /**
    147      *
    148      */
    149115    public static final int TAG_OLYMPUS_PICT_INFO = 0x0208;
    150 
    151     /**
    152      *
    153      */
    154116    public static final int TAG_OLYMPUS_CAMERA_ID = 0x0209;
    155117
     
    166128    public static final int TAG_OLYMPUS_IMAGE_HEIGHT = 0x020C;
    167129
    168     /**
    169      * A string. Used by Epson cameras.
    170      */
     130    /** A string. Used by Epson cameras. */
    171131    public static final int TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL = 0x020D;
    172132
     
    177137    public static final int TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    178138
    179     /**
    180      *
    181      */
    182139    public static final int TAG_OLYMPUS_DATA_DUMP = 0x0F00;
    183 
    184     /**
    185      *
    186      */
    187140    public static final int TAG_OLYMPUS_FLASH_MODE = 0x1004;
    188 
    189     /**
    190      *
    191      */
    192141    public static final int TAG_OLYMPUS_BRACKET = 0x1006;
    193 
    194     /**
    195      *
    196      */
    197142    public static final int TAG_OLYMPUS_FOCUS_MODE = 0x100B;
    198 
    199     /**
    200      *
    201      */
    202143    public static final int TAG_OLYMPUS_FOCUS_DISTANCE = 0x100C;
    203 
    204     /**
    205      *
    206      */
    207144    public static final int TAG_OLYMPUS_ZOOM = 0x100D;
    208 
    209     /**
    210      *
    211      */
    212145    public static final int TAG_OLYMPUS_MACRO_FOCUS = 0x100E;
    213 
    214     /**
    215      *
    216      */
    217146    public static final int TAG_OLYMPUS_SHARPNESS = 0x100F;
    218 
    219     /**
    220      *
    221      */
    222147    public static final int TAG_OLYMPUS_COLOUR_MATRIX = 0x1011;
    223 
    224     /**
    225      *
    226      */
    227148    public static final int TAG_OLYMPUS_BLACK_LEVEL = 0x1012;
    228 
    229     /**
    230      *
    231      */
    232149    public static final int TAG_OLYMPUS_WHITE_BALANCE = 0x1015;
    233 
    234     /**
    235      *
    236      */
    237150    public static final int TAG_OLYMPUS_RED_BIAS = 0x1017;
    238 
    239     /**
    240      *
    241      */
    242151    public static final int TAG_OLYMPUS_BLUE_BIAS = 0x1018;
    243 
    244     /**
    245      *
    246      */
    247152    public static final int TAG_OLYMPUS_SERIAL_NUMBER = 0x101A;
    248 
    249     /**
    250      *
    251      */
    252153    public static final int TAG_OLYMPUS_FLASH_BIAS = 0x1023;
    253 
    254     /**
    255      *
    256      */
    257154    public static final int TAG_OLYMPUS_CONTRAST = 0x1029;
    258 
    259     /**
    260      *
    261      */
    262155    public static final int TAG_OLYMPUS_SHARPNESS_FACTOR = 0x102A;
    263 
    264     /**
    265      *
    266      */
    267156    public static final int TAG_OLYMPUS_COLOUR_CONTROL = 0x102B;
    268 
    269     /**
    270      *
    271      */
    272157    public static final int TAG_OLYMPUS_VALID_BITS = 0x102C;
    273 
    274     /**
    275      *
    276      */
    277158    public static final int TAG_OLYMPUS_CORING_FILTER = 0x102D;
    278 
    279     /**
    280      *
    281      */
    282159    public static final int TAG_OLYMPUS_FINAL_WIDTH = 0x102E;
    283 
    284     /**
    285      *
    286      */
    287160    public static final int TAG_OLYMPUS_FINAL_HEIGHT = 0x102F;
    288 
    289     /**
    290      *
    291      */
    292161    public static final int TAG_OLYMPUS_COMPRESSION_RATIO = 0x1034;
    293162
    294     protected static final HashMap tagNameMap = new HashMap();
    295 
    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");
     163    @NotNull
     164    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     165
     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");
    344214    }
    345215
     
    349219    }
    350220
     221    @NotNull
    351222    public String getName()
    352223    {
     
    354225    }
    355226
    356     protected HashMap getTagNameMap()
     227    @NotNull
     228    protected HashMap<Integer, String> getTagNameMap()
    357229    {
    358         return tagNameMap;
     230        return _tagNameMap;
    359231    }
    360232}
  • trunk/src/com/drew/metadata/exif/PanasonicMakernoteDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.ByteArrayReader;
     26import com.drew.lang.annotations.NotNull;
     27import com.drew.lang.annotations.Nullable;
     28import com.drew.metadata.Age;
     29import com.drew.metadata.Face;
    1930import com.drew.metadata.TagDescriptor;
    2031
     32import java.io.UnsupportedEncodingException;
     33
    2134/**
    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>
    2342 *
    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
    2644 */
    27 public class PanasonicMakernoteDescriptor extends TagDescriptor
     45public class PanasonicMakernoteDescriptor extends TagDescriptor<PanasonicMakernoteDirectory>
    2846{
    29     public PanasonicMakernoteDescriptor(Directory directory)
     47    public PanasonicMakernoteDescriptor(@NotNull PanasonicMakernoteDirectory directory)
    3048    {
    3149        super(directory);
    3250    }
    3351
    34     public String getDescription(int tagType) throws MetadataException
    35     {
    36         switch (tagType)
     52    @Nullable
     53    public String getDescription(int tagType)
     54    {
     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:
     69                return getMacroModeDescription();
     70            case PanasonicMakernoteDirectory.TAG_RECORD_MODE:
     71                return getRecordModeDescription();
     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:
     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";
     219            default:
     220                return "Unknown (" + value + ")";
     221        }
     222    }
     223
     224    @Nullable
     225    public String getAudioDescription()
     226    {
     227        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_AUDIO);
     228    }
     229
     230    @Nullable
     231    public String getTransformDescription()
     232    {
     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
    37252        {
    38             case PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE:
    39                 return getMacroModeDescription();
    40             case PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE:
    41                 return getRecordModeDescription();
    42             case PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO:
    43                 return getPrintImageMatchingInfoDescription();
    44             default:
    45                 return _directory.getString(tagType);
    46         }
    47     }
    48 
    49     public String getPrintImageMatchingInfoDescription() throws MetadataException
    50     {
    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)";
    54     }
    55 
    56     public String getMacroModeDescription() throws MetadataException
    57     {
    58         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null;
    59         int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
    60         switch (value) {
    61             case 1:
     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;
     279        switch (value) {
     280            case 0:
     281                return "Off";
     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:
    62364                return "On";
    63             case 2:
     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";
     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:
    64453                return "Off";
    65             default:
    66                 return "Unknown (" + value + ")";
    67         }
    68     }
    69 
    70     public String getRecordModeDescription() throws MetadataException
    71     {
    72         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null;
    73         int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
    74         switch (value) {
    75             case 1:
     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";
     462            default:
     463                return "Unknown (" + value + ")";
     464        }
     465    }
     466
     467    @Nullable
     468    public String getUptimeDescription()
     469    {
     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;
     482        switch (value) {
     483            case 0:
     484                return "Off";
     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:
    76504                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)";
     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";
    77698            case 2:
    78699                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";
    79712            case 9:
    80713                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";
     786            default:
     787                return "Unknown (" + value + ")";
     788        }
     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";
    811099            default:
    821100                return "Unknown (" + value + ")";
  • trunk/src/com/drew/metadata/exif/PanasonicMakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.ByteArrayReader;
     26import com.drew.lang.annotations.NotNull;
     27import com.drew.lang.annotations.Nullable;
     28import com.drew.metadata.Age;
    1929import com.drew.metadata.Directory;
     30import com.drew.metadata.Face;
    2031
    2132import java.util.HashMap;
    2233
    2334/**
     35 * Describes tags specific to Panasonic and Leica cameras.
    2436 *
     37 * @author Drew Noakes http://drewnoakes.com, Philipp Sandhaus
    2538 */
    2639public class PanasonicMakernoteDirectory extends Directory
    2740{
    28     public static final int TAG_PANASONIC_QUALITY_MODE = 0x0001;
    29     public static final int TAG_PANASONIC_VERSION = 0x0002;
    30     /**
    31      * 1 = On
    32      * 2 = Off
    33      */
    34     public static final int TAG_PANASONIC_MACRO_MODE = 0x001C;
    35     /**
    36      * 1 = Normal
    37      * 2 = Portrait
    38      * 9 = Macro
    39      */
    40     public static final int TAG_PANASONIC_RECORD_MODE = 0x001F;
    41     public static final int TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    42 
    43     protected static final HashMap tagNameMap = new HashMap();
     41
     42    /**
     43     * <br>
     44     * 2 = High            <br>
     45     * 3 = Normal          <br>
     46     * 6 = Very High       <br>
     47     * 7 = Raw             <br>
     48     * 9 = Motion Picture  <br>
     49     */
     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
     77    /**
     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>
     96     */
     97    public static final int TAG_AF_AREA_MODE = 0x000f;
     98
     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;
     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>();
    44473
    45474    static
    46475    {
    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");
    52545    }
    53546
     
    57550    }
    58551
     552    @NotNull
    59553    public String getName()
    60554    {
     
    62556    }
    63557
    64     protected HashMap getTagNameMap()
     558    @NotNull
     559    protected HashMap<Integer, String> getTagNameMap()
    65560    {
    66         return tagNameMap;
     561        return _tagNameMap;
    67562    }
     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        }
    68642}
  • trunk/src/com/drew/metadata/exif/PentaxMakernoteDescriptor.java

    r4231 r6127  
    11/*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    1420 */
    1521package com.drew.metadata.exif;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
    2127/**
    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/>
    2430 * Some information about this makernote taken from here:
    2531 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/pentax_mn.html
     32 *
     33 * @author Drew Noakes http://drewnoakes.com
    2634 */
    27 public class PentaxMakernoteDescriptor extends TagDescriptor
     35public class PentaxMakernoteDescriptor extends TagDescriptor<PentaxMakernoteDirectory>
    2836{
    29     public PentaxMakernoteDescriptor(Directory directory)
     37    public PentaxMakernoteDescriptor(@NotNull PentaxMakernoteDirectory directory)
    3038    {
    3139        super(directory);
    3240    }
    3341
    34     public String getDescription(int tagType) throws MetadataException
     42    @Nullable
     43    public String getDescription(int tagType)
    3544    {
    3645        switch (tagType)
     
    5968                return getColourDescription();
    6069            default:
    61                 return _directory.getString(tagType);
    62         }
    63     }
    64 
    65     public String getColourDescription() throws MetadataException
    66     {
    67         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR)) return null;
    68         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR);
     70                return super.getDescription(tagType);
     71        }
     72    }
     73
     74    @Nullable
     75    public String getColourDescription()
     76    {
     77        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR);
     78        if (value==null)
     79            return null;
    6980        switch (value)
    7081        {
     
    7687    }
    7788
    78     public String getIsoSpeedDescription() throws MetadataException
    79     {
    80         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED)) return null;
    81         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED);
     89    @Nullable
     90    public String getIsoSpeedDescription()
     91    {
     92        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED);
     93        if (value==null)
     94            return null;
    8295        switch (value)
    8396        {
     
    91104    }
    92105
    93     public String getSaturationDescription() throws MetadataException
    94     {
    95         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION)) return null;
    96         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION);
     106    @Nullable
     107    public String getSaturationDescription()
     108    {
     109        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION);
     110        if (value==null)
     111            return null;
    97112        switch (value)
    98113        {
     
    104119    }
    105120
    106     public String getContrastDescription() throws MetadataException
    107     {
    108         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST)) return null;
    109         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST);
     121    @Nullable
     122    public String getContrastDescription()
     123    {
     124        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST);
     125        if (value==null)
     126            return null;
    110127        switch (value)
    111128        {
     
    117134    }
    118135
    119     public String getSharpnessDescription() throws MetadataException
    120     {
    121         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS)) return null;
    122         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS);
     136    @Nullable
     137    public String getSharpnessDescription()
     138    {
     139        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS);
     140        if (value==null)
     141            return null;
    123142        switch (value)
    124143        {
     
    130149    }
    131150
    132     public String getDigitalZoomDescription() throws MetadataException
    133     {
    134         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM)) return null;
    135         float value = _directory.getFloat(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM);
     151    @Nullable
     152    public String getDigitalZoomDescription()
     153    {
     154        Float value = _directory.getFloatObject(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM);
     155        if (value==null)
     156            return null;
    136157        if (value==0)
    137158            return "Off";
     
    139160    }
    140161
    141     public String getWhiteBalanceDescription() throws MetadataException
    142     {
    143         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE)) return null;
    144         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE);
     162    @Nullable
     163    public String getWhiteBalanceDescription()
     164    {
     165        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE);
     166        if (value==null)
     167            return null;
    145168        switch (value)
    146169        {
     
    155178    }
    156179
    157     public String getFlashModeDescription() throws MetadataException
    158     {
    159         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE)) return null;
    160         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE);
     180    @Nullable
     181    public String getFlashModeDescription()
     182    {
     183        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE);
     184        if (value==null)
     185            return null;
    161186        switch (value)
    162187        {
     
    169194    }
    170195
    171     public String getFocusModeDescription() throws MetadataException
    172     {
    173         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE)) return null;
    174         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE);
     196    @Nullable
     197    public String getFocusModeDescription()
     198    {
     199        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE);
     200        if (value==null)
     201            return null;
    175202        switch (value)
    176203        {
     
    181208    }
    182209
    183     public String getQualityLevelDescription() throws MetadataException
    184     {
    185         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL)) return null;
    186         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL);
     210    @Nullable
     211    public String getQualityLevelDescription()
     212    {
     213        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL);
     214        if (value==null)
     215            return null;
    187216        switch (value)
    188217        {
     
    194223    }
    195224
    196     public String getCaptureModeDescription() throws MetadataException
    197     {
    198         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE)) return null;
    199         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE);
     225    @Nullable
     226    public String getCaptureModeDescription()
     227    {
     228        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE);
     229        if (value==null)
     230            return null;
    200231        switch (value)
    201232        {
     
    209240
    210241/*
    211     public String getPrintImageMatchingInfoDescription() throws MetadataException
    212     {
    213         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null;
     242    public String getPrintImageMatchingInfoDescription()
     243    {
    214244        byte[] bytes = _directory.getByteArray(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO);
     245        if (bytes==null)
     246            return null;
    215247        return "(" + bytes.length + " bytes)";
    216248    }
    217249
    218     public String getMacroModeDescription() throws MetadataException
    219     {
    220         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null;
    221         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     250    public String getMacroModeDescription()
     251    {
     252        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     253        if (value==null)
     254            return null;
    222255        switch (value) {
    223256            case 1:
     
    230263    }
    231264
    232     public String getRecordModeDescription() throws MetadataException
    233     {
    234         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null;
    235         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     265    public String getRecordModeDescription()
     266    {
     267        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     268        if (value==null)
     269            return null;
    236270        switch (value) {
    237271            case 1:
  • trunk/src/com/drew/metadata/exif/PentaxMakernoteDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.exif;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
    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
    2532 */
    2633public class PentaxMakernoteDirectory extends Directory
     
    122129    public static final int TAG_PENTAX_DAYLIGHT_SAVINGS = 0x1001;
    123130
    124     protected static final HashMap tagNameMap = new HashMap();
     131    @NotNull
     132    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    125133
    126134    static
    127135    {
    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");
    142150    }
    143151
     
    147155    }
    148156
     157    @NotNull
    149158    public String getName()
    150159    {
     
    152161    }
    153162
    154     protected HashMap getTagNameMap()
     163    @NotNull
     164    protected HashMap<Integer, String> getTagNameMap()
    155165    {
    156         return tagNameMap;
     166        return _tagNameMap;
    157167    }
    158168}
  • trunk/src/com/drew/metadata/iptc/IptcDescriptor.java

    r4231 r6127  
    11/*
    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 21-Nov-2002 17:58:19 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.iptc;
    1822
    19 import com.drew.metadata.Directory;
     23import com.drew.lang.StringUtil;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
    2026import com.drew.metadata.TagDescriptor;
    2127
    2228/**
    23  *
     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.
     32 *
     33 * @author Drew Noakes http://drewnoakes.com
    2434 */
    25 public class IptcDescriptor extends TagDescriptor
     35public class IptcDescriptor extends TagDescriptor<IptcDirectory>
    2636{
    27     public IptcDescriptor(Directory directory)
     37    public IptcDescriptor(@NotNull IptcDirectory directory)
    2838    {
    2939        super(directory);
    3040    }
    3141
     42    @Nullable
    3243    public String getDescription(int tagType)
    3344    {
    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        }
     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);
    35241    }
    36242}
  • trunk/src/com/drew/metadata/iptc/IptcDirectory.java

    r4231 r6127  
    11/*
    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 26-Nov-2002 01:26:39 using IntelliJ IDEA.
     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/
    1620 */
    1721package com.drew.metadata.iptc;
    1822
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.Directory;
    2026
     27import java.util.Arrays;
    2128import java.util.HashMap;
     29import java.util.List;
    2230
    2331/**
    24  *
     32 * Describes tags used by the International Press Telecommunications Council (IPTC) metadata format.
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
    2535 */
    2636public class IptcDirectory extends Directory
    2737{
    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;
    52 
    53     protected static final HashMap tagNameMap = new HashMap();
     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
     53
     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
     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>();
    54123
    55124    static
    56125    {
    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");
    81206    }
    82207
     
    86211    }
    87212
     213    @NotNull
    88214    public String getName()
    89215    {
     
    91217    }
    92218
    93     protected HashMap getTagNameMap()
    94     {
    95         return tagNameMap;
     219    @NotNull
     220    protected HashMap<Integer, String> getTagNameMap()
     221    {
     222        return _tagNameMap;
     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);
    96235    }
    97236}
  • trunk/src/com/drew/metadata/iptc/IptcReader.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.iptc;
    1822
    19 import com.drew.imaging.jpeg.JpegProcessingException;
    20 import com.drew.imaging.jpeg.JpegSegmentReader;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.annotations.NotNull;
    2126import com.drew.metadata.Directory;
    2227import com.drew.metadata.Metadata;
    23 import com.drew.metadata.MetadataException;
    2428import com.drew.metadata.MetadataReader;
    2529
    26 import java.io.File;
    27 import java.io.InputStream;
    2830import java.util.Date;
    2931
    3032/**
     33 * Decodes IPTC binary data, populating a <code>Metadata</code> object with tag values in an <code>IptcDirectory</code>.
    3134 *
     35 * @author Drew Noakes http://drewnoakes.com
    3236 */
    3337public class IptcReader implements MetadataReader
    3438{
     39    // TODO consider breaking the IPTC section up into multiple directories and providing segregation of each IPTC directory
    3540/*
    3641    public static final int DIRECTORY_IPTC = 2;
     
    4651    public static final int POST_DATA_RECORD = 9;
    4752*/
    48     /**
    49      * The Iptc data segment.
    50      */
    51     private final byte[] _data;
    5253
    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)
    5756    {
    58         this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_APPD));
    59     }
     57        IptcDirectory directory = metadata.getOrCreateDirectory(IptcDirectory.class);
    6058
    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;
    6960
    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;
     61/*
     62        // find start-of-segment marker (potentially need to skip some ASCII photoshop header info)
     63        try {
     64            while (offset < data.length - 1 && reader.getUInt16(offset) != 0x1c01 && reader.getUInt16(offset) != 0x1c02)
     65                offset++;
     66        } catch (BufferBoundsException e) {
     67            directory.addError("Couldn't find start of IPTC data (invalid segment)");
     68            return;
    9169        }
    92 
    93         Directory directory = metadata.getDirectory(IptcDirectory.class);
    94 
    95         // find start of data
    96         int offset = 0;
    97         try {
    98             while (offset < _data.length - 1 && get32Bits(offset) != 0x1c02) {
    99                 offset++;
    100             }
    101         } catch (MetadataException e) {
    102             directory.addError("Couldn't find start of Iptc data (invalid segment)");
    103             return metadata;
    104         }
     70*/
    10571
    10672        // for each tag
    107         while (offset < _data.length) {
     73        while (offset < reader.getLength()) {
     74
    10875            // 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");
    11081                break;
    11182            }
     83
     84            if (startByte != 0x1c) {
     85                directory.addError("Invalid start to IPTC tag");
     86                break;
     87            }
     88
    11289            // 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");
    11492                break;
    11593            }
     
    12199            int tagByteCount;
    122100            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;
    129108            }
    130             offset += 2;
    131             if ((offset + tagByteCount) > _data.length) {
    132                 directory.addError("data for tag extends beyond end of iptc segment");
     109
     110            if (offset + tagByteCount > reader.getLength()) {
     111                directory.addError("Data for tag extends beyond end of IPTC segment");
    133112                break;
    134113            }
    135114
    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
    137122            offset += tagByteCount;
    138123        }
    139 
    140         return metadata;
    141124    }
    142125
    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
    149     {
    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 IPTC
    158      * octets to relevant java object.
    159      */
    160     private void processTag(Directory directory, int directoryType, int tagType, int offset, int tagByteCount)
     126    private void processTag(@NotNull BufferReader reader, @NotNull Directory directory, int directoryType, int tagType, int offset, int tagByteCount) throws BufferBoundsException
    161127    {
    162128        int tagIdentifier = tagType | (directoryType << 8);
    163129
    164130        switch (tagIdentifier) {
    165             case IptcDirectory.TAG_RECORD_VERSION:
     131            case IptcDirectory.TAG_APPLICATION_RECORD_VERSION:
    166132                // short
    167                 short shortValue = (short)((_data[offset] << 8) | _data[offset + 1]);
     133                int shortValue = reader.getUInt16(offset);
    168134                directory.setInt(tagIdentifier, shortValue);
    169135                return;
    170136            case IptcDirectory.TAG_URGENCY:
    171137                // byte
    172                 directory.setInt(tagIdentifier, _data[offset]);
     138                directory.setInt(tagIdentifier, reader.getUInt8(offset));
    173139                return;
    174140            case IptcDirectory.TAG_RELEASE_DATE:
     
    176142                // Date object
    177143                if (tagByteCount >= 8) {
    178                     String dateStr = new String(_data, offset, tagByteCount);
     144                    String dateStr = reader.getString(offset, tagByteCount);
    179145                    try {
    180146                        int year = Integer.parseInt(dateStr.substring(0, 4));
    181147                        int month = Integer.parseInt(dateStr.substring(4, 6)) - 1;
    182148                        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();
    184150                        directory.setDate(tagIdentifier, date);
    185151                        return;
     
    194160                // fall through
    195161        }
    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
    197164        String str;
    198165        if (tagByteCount < 1) {
    199166            str = "";
    200167        } else {
    201             str = new String(_data, offset, tagByteCount);
     168            str = reader.getString(offset, tagByteCount, System.getProperty("file.encoding")); // "ISO-8859-1"
    202169        }
     170
    203171        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);
    205174            String[] newStrings;
    206             try {
    207                 oldStrings = directory.getStringArray(tagIdentifier);
    208             } catch (MetadataException e) {
    209                 oldStrings = null;
    210             }
    211175            if (oldStrings == null) {
    212176                newStrings = new String[1];
    213177            } else {
    214178                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);
    218180            }
    219181            newStrings[newStrings.length - 1] = str;
  • trunk/src/com/drew/metadata/jpeg/JpegCommentDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    15  * Created by dnoakes on Oct 10, 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/
    1620 */
    1721package com.drew.metadata.jpeg;
    1822
    19 import com.drew.metadata.Directory;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    2025import com.drew.metadata.TagDescriptor;
    2126
    2227/**
     28 * Provides human-readable string representations of tag values stored in a <code>JpegCommentDirectory</code>.
    2329 *
    2430 * @author Drew Noakes http://drewnoakes.com
    2531 */
    26 public class JpegCommentDescriptor extends TagDescriptor
     32public class JpegCommentDescriptor extends TagDescriptor<JpegCommentDirectory>
    2733{
    28     public JpegCommentDescriptor(Directory directory)
     34    public JpegCommentDescriptor(@NotNull JpegCommentDirectory directory)
    2935    {
    3036        super(directory);
    3137    }
    3238
    33     public String getDescription(int tagType)
     39    @Nullable
     40    public String getJpegCommentDescription()
    3441    {
    35         return _directory.getString(tagType);
     42        return _directory.getString(JpegCommentDirectory.TAG_JPEG_COMMENT);
    3643    }
    3744}
  • trunk/src/com/drew/metadata/jpeg/JpegCommentDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    15  * Created by dnoakes on Oct 10, 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/
    1620 */
    1721package com.drew.metadata.jpeg;
    1822
     23import com.drew.lang.annotations.NotNull;
    1924import com.drew.metadata.Directory;
    2025
     
    2227
    2328/**
     29 * Describes tags used by a JPEG file comment.
    2430 *
    2531 * @author Drew Noakes http://drewnoakes.com
    2632 */
    27 public class JpegCommentDirectory extends Directory {
     33public 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;
    2840
    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;
     41    @NotNull
     42    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    3143
    32         protected static final HashMap tagNameMap = new HashMap();
     44    static {
     45        _tagNameMap.put(TAG_JPEG_COMMENT, "Jpeg Comment");
     46    }
    3347
    34         static {
    35         tagNameMap.put(new Integer(TAG_JPEG_COMMENT), "Jpeg Comment");
    36         }
     48    public JpegCommentDirectory()
     49    {
     50        this.setDescriptor(new JpegCommentDescriptor(this));
     51    }
    3752
    38     public JpegCommentDirectory() {
    39                 this.setDescriptor(new JpegCommentDescriptor(this));
    40         }
     53    @NotNull
     54    public String getName()
     55    {
     56        return "JpegComment";
     57    }
    4158
    42         public String getName() {
    43                 return "JpegComment";
    44         }
    45 
    46         protected HashMap getTagNameMap() {
    47                 return tagNameMap;
    48         }
     59    @NotNull
     60    protected HashMap<Integer, String> getTagNameMap()
     61    {
     62        return _tagNameMap;
     63    }
    4964}
  • trunk/src/com/drew/metadata/jpeg/JpegCommentReader.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    15  * Created by dnoakes on Oct 10, 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/
    1620 */
    1721package com.drew.metadata.jpeg;
    1822
    19 import com.drew.imaging.jpeg.JpegProcessingException;
    20 import com.drew.imaging.jpeg.JpegSegmentReader;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.annotations.NotNull;
    2126import com.drew.metadata.Metadata;
    2227import com.drew.metadata.MetadataReader;
    2328
    24 import java.io.File;
    25 import java.io.InputStream;
    26 
    2729/**
     30 * Decodes the comment stored within Jpeg files, populating a <code>Metadata</code> object with tag values in a
     31 * <code>JpegCommentDirectory</code>.
    2832 *
    2933 * @author Drew Noakes http://drewnoakes.com
     
    3236{
    3337    /**
    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     /**
    6938     * Performs the Jpeg data extraction, adding found values to the specified
    7039     * instance of <code>Metadata</code>.
    7140     */
    72     public Metadata extract(Metadata metadata)
     41    public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
    7342    {
    74         if (_data==null) {
    75             return metadata;
     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");
    7649        }
    77 
    78         JpegCommentDirectory directory = (JpegCommentDirectory)metadata.getDirectory(JpegCommentDirectory.class);
    79 
    80         directory.setString(JpegCommentDirectory.TAG_JPEG_COMMENT, new String(_data));
    81 
    82         return metadata;
    8350    }
    8451}
  • trunk/src/com/drew/metadata/jpeg/JpegComponent.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    15  * Created by dnoakes on Oct 9, 17:04:07 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/
    1620 */
    1721package com.drew.metadata.jpeg;
    1822
    19 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.Nullable;
    2024
    2125import java.io.Serializable;
    2226
    2327/**
    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.
     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
    2932 */
    3033public class JpegComponent implements Serializable
    3134{
     35    private static final long serialVersionUID = 61121257899091914L;
     36
    3237    private final int _componentId;
    3338    private final int _samplingFactorByte;
     
    4651    }
    4752
    48     public String getComponentName() throws MetadataException
     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()
    4959    {
    5060        switch (_componentId)
     
    6171                return "Q";
    6272        }
    63 
    64         throw new MetadataException("Unsupported component id: " + _componentId);
     73        return null;
    6574    }
    6675
  • trunk/src/com/drew/metadata/jpeg/JpegDescriptor.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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/
    1420 */
    1521package com.drew.metadata.jpeg;
    1622
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.TagDescriptor;
    2026
     
    2228 * Provides human-readable string versions of the tags stored in a JpegDirectory.
    2329 * Thanks to Darrell Silver (www.darrellsilver.com) for the initial version of this class.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
    2432 */
    25 public class JpegDescriptor extends TagDescriptor
     33public class JpegDescriptor extends TagDescriptor<JpegDirectory>
    2634{
    27     public JpegDescriptor(Directory directory)
     35    public JpegDescriptor(@NotNull JpegDirectory directory)
    2836    {
    2937        super(directory);
    3038    }
    3139
    32     public String getDescription(int tagType) throws MetadataException
     40    @Nullable
     41    public String getDescription(int tagType)
    3342    {
    3443        switch (tagType)
    3544        {
     45            case JpegDirectory.TAG_JPEG_COMPRESSION_TYPE:
     46                return getImageCompressionTypeDescription();
    3647            case JpegDirectory.TAG_JPEG_COMPONENT_DATA_1:
    3748                return getComponentDataDescription(0);
     
    4859            case JpegDirectory.TAG_JPEG_IMAGE_WIDTH:
    4960                return getImageWidthDescription();
     61            default:
     62                return super.getDescription(tagType);
    5063        }
    51 
    52         return _directory.getString(tagType);
    5364    }
    5465
     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        }
     91    }
     92    @Nullable
    5593    public String getImageWidthDescription()
    5694    {
    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";
    5899    }
    59100
     101    @Nullable
    60102    public String getImageHeightDescription()
    61103    {
    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";
    63108    }
    64109
     110    @Nullable
    65111    public String getDataPrecisionDescription()
    66112    {
    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";
    68117    }
    69118
    70     public String getComponentDataDescription(int componentNumber) throws MetadataException
     119    @Nullable
     120    public String getComponentDataDescription(int componentNumber)
    71121    {
    72         JpegComponent component = ((JpegDirectory)_directory).getComponent(componentNumber);
     122        JpegComponent value = _directory.getComponent(componentNumber);
    73123
    74         if (component==null)
    75             throw new MetadataException("No Jpeg component exists with number " + componentNumber);
     124        if (value==null)
     125            return null;
    76126
    77         StringBuffer sb = new StringBuffer();
    78         sb.append(component.getComponentName());
     127        StringBuilder sb = new StringBuilder();
     128        sb.append(value.getComponentName());
    79129        sb.append(" component: Quantization table ");
    80         sb.append(component.getQuantizationTableNumber());
     130        sb.append(value.getQuantizationTableNumber());
    81131        sb.append(", Sampling factors ");
    82         sb.append(component.getHorizontalSamplingFactor());
     132        sb.append(value.getHorizontalSamplingFactor());
    83133        sb.append(" horiz/");
    84         sb.append(component.getVerticalSamplingFactor());
     134        sb.append(value.getVerticalSamplingFactor());
    85135        sb.append(" vert");
    86136        return sb.toString();
  • trunk/src/com/drew/metadata/jpeg/JpegDirectory.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.jpeg;
    1822
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
    1925import com.drew.metadata.Directory;
    2026import com.drew.metadata.MetadataException;
     
    2430/**
    2531 * 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
    2734 */
    28 public class JpegDirectory extends Directory {
    29 
    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 CMYK
    37          * 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.htm
    43          */
    44         public static final int TAG_JPEG_NUMBER_OF_COMPONENTS = 5;
     35public 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;
    4554
    4655    // NOTE!  Component tag type int values must increment in steps of 1
    4756
    48         /** the first of a possible 4 color components.  Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/
    49         public static final int TAG_JPEG_COMPONENT_DATA_1 = 6;
    50         /** the second of a possible 4 color components.  Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/
    51         public static final int TAG_JPEG_COMPONENT_DATA_2 = 7;
    52         /** the third of a possible 4 color components.  Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/
    53         public static final int TAG_JPEG_COMPONENT_DATA_3 = 8;
    54         /** the fourth of a possible 4 color components.  Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/
    55         public static final int TAG_JPEG_COMPONENT_DATA_4 = 9;
     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;
    5665
    57         protected static final HashMap tagNameMap = new HashMap();
     66    @NotNull
     67    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    5868
    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    }
    6980
    70     public JpegDirectory() {
    71                 this.setDescriptor(new JpegDescriptor(this));
    72         }
     81    public JpegDirectory()
     82    {
     83        this.setDescriptor(new JpegDescriptor(this));
     84    }
    7385
    74         public String getName() {
    75                 return "Jpeg";
    76         }
     86    @NotNull
     87    public String getName()
     88    {
     89        return "Jpeg";
     90    }
    7791
    78         protected HashMap getTagNameMap() {
    79                 return tagNameMap;
    80         }
     92    @NotNull
     93    protected HashMap<Integer, String> getTagNameMap()
     94    {
     95        return _tagNameMap;
     96    }
    8197
    8298    /**
    83      *
    8499     * @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.
    87102     */
     103    @Nullable
    88104    public JpegComponent getComponent(int componentNumber)
    89105    {
    90106        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);
    95108    }
    96109
  • trunk/src/com/drew/metadata/jpeg/JpegReader.java

    r4231 r6127  
    11/*
    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
    63 *
    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
    97 *
    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
    149 *
    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/
    1620 */
    1721package com.drew.metadata.jpeg;
    1822
    19 import com.drew.imaging.jpeg.JpegProcessingException;
    20 import com.drew.imaging.jpeg.JpegSegmentReader;
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.annotations.NotNull;
    2126import com.drew.metadata.Metadata;
    22 import com.drew.metadata.MetadataException;
    2327import com.drew.metadata.MetadataReader;
    2428
    25 import java.io.File;
    26 import java.io.InputStream;
    27 
    2829/**
     30 * Decodes Jpeg SOF0 data, populating a <code>Metadata</code> object with tag values in a <code>JpegDirectory</code>.
    2931 *
    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
    3133 */
    3234public class JpegReader implements MetadataReader
    3335{
    3436    /**
    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 JpegProcessingException
    43     {
    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 JpegProcessingException
    52     {
    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     /**
    7037     * Performs the Jpeg data extraction, adding found values to the specified
    7138     * instance of <code>Metadata</code>.
    7239     */
    73     public Metadata extract(Metadata metadata)
     40    public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
    7441    {
    75         if (_data==null) {
    76             return metadata;
    77         }
    78 
    79         JpegDirectory directory = (JpegDirectory)metadata.getDirectory(JpegDirectory.class);
     42        JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class);
    8043
    8144        try {
    8245            // data precision
    83             int dataPrecision = get16Bits(JpegDirectory.TAG_JPEG_DATA_PRECISION);
     46            int dataPrecision = reader.getUInt8(JpegDirectory.TAG_JPEG_DATA_PRECISION);
    8447            directory.setInt(JpegDirectory.TAG_JPEG_DATA_PRECISION, dataPrecision);
    8548
    8649            // process height
    87             int height = get32Bits(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT);
     50            int height = reader.getUInt16(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT);
    8851            directory.setInt(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT, height);
    8952
    9053            // process width
    91             int width = get32Bits(JpegDirectory.TAG_JPEG_IMAGE_WIDTH);
     54            int width = reader.getUInt16(JpegDirectory.TAG_JPEG_IMAGE_WIDTH);
    9255            directory.setInt(JpegDirectory.TAG_JPEG_IMAGE_WIDTH, width);
    9356
    9457            // number of components
    95             int numberOfComponents = get16Bits(JpegDirectory.TAG_JPEG_NUMBER_OF_COMPONENTS);
     58            int numberOfComponents = reader.getUInt8(JpegDirectory.TAG_JPEG_NUMBER_OF_COMPONENTS);
    9659            directory.setInt(JpegDirectory.TAG_JPEG_NUMBER_OF_COMPONENTS, numberOfComponents);
    9760
     
    10164            // 3 - Quantization table number
    10265            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++);
    10870                JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
    10971                directory.setObject(JpegDirectory.TAG_JPEG_COMPONENT_DATA_1 + i, component);
    11072            }
    11173
    112         } catch (MetadataException me) {
    113             directory.addError("MetadataException: " + me);
     74        } catch (BufferBoundsException ex) {
     75            directory.addError(ex.getMessage());
    11476        }
    115 
    116         return metadata;
    117     }
    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 byte
    122      * @return the 32 bit int value, between 0x0000 and 0xFFFF
    123      */
    124     private int get32Bits(int offset) throws MetadataException
    125     {
    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 byte
    136      * @return the 16 bit int value, between 0x00 and 0xFF
    137      */
    138     private int get16Bits(int offset) throws MetadataException
    139     {
    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);
    14577    }
    14678}
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    r6106 r6127  
    6565import com.drew.imaging.jpeg.JpegMetadataReader;
    6666import com.drew.lang.CompoundException;
     67import com.drew.lang.GeoLocation;
    6768import com.drew.lang.Rational;
    6869import com.drew.metadata.Directory;
    6970import com.drew.metadata.Metadata;
    7071import com.drew.metadata.MetadataException;
    71 import com.drew.metadata.exif.ExifDirectory;
     72import com.drew.metadata.exif.ExifIFD0Directory;
    7273import com.drew.metadata.exif.GpsDirectory;
    7374
     
    508509    private static void extractExif(ImageEntry e) {
    509510
    510         double deg;
    511         double min, sec;
    512         double lon, lat;
    513         Metadata metadata = null;
    514         Directory dirExif = null, dirGps = null;
     511        Metadata metadata;
     512        Directory dirExif;
     513        GpsDirectory dirGps;
    515514
    516515        try {
    517516            metadata = JpegMetadataReader.readMetadata(e.getFile());
    518             dirExif = metadata.getDirectory(ExifDirectory.class);
     517            dirExif = metadata.getDirectory(ExifIFD0Directory.class);
    519518            dirGps = metadata.getDirectory(GpsDirectory.class);
    520519        } catch (CompoundException p) {
     
    522521            e.setPos(null);
    523522            return;
     523        } catch (IOException p) {
     524            e.setExifCoor(null);
     525            e.setPos(null);
     526            return;
    524527        }
    525528
    526529        try {
    527             int orientation = dirExif.getInt(ExifDirectory.TAG_ORIENTATION);
     530            int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    528531            e.setExifOrientation(orientation);
    529532        } catch (MetadataException ex) {
     533        }
     534
     535        if (dirGps == null) {
     536            e.setExifCoor(null);
     537            e.setPos(null);
     538            return;
    530539        }
    531540
     
    541550
    542551        try {
    543             // longitude
    544 
    545             Rational[] components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
    546 
    547             deg = components[0].doubleValue();
    548             min = components[1].doubleValue();
    549             sec = components[2].doubleValue();
    550 
    551             if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
    552                 throw new IllegalArgumentException();
    553 
    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;
    558             }
    559 
    560             // latitude
    561 
    562             components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
    563 
    564             deg = components[0].doubleValue();
    565             min = components[1].doubleValue();
    566             sec = components[2].doubleValue();
    567 
    568             if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
    569                 throw new IllegalArgumentException();
    570 
    571             lat = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
    572 
    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;
    578             }
    579 
    580             // Store values
    581 
    582             e.setExifCoor(new LatLon(lat, lon));
    583             e.setPos(e.getExifCoor());
    584 
    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 values
    594 
    595                 e.setExifCoor(new LatLon(latitude, longitude));
     552            GeoLocation location = dirGps.getGeoLocation();
     553            if (location != null) {
     554                e.setExifCoor(new LatLon(location.getLatitude(), location.getLongitude()));
    596555                e.setPos(e.getExifCoor());
    597             } catch (CompoundException ex) {
    598                 e.setExifCoor(null);
    599                 e.setPos(null);
     556            } else {
     557                // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)
     558                try {
     559                    Double longitude = dirGps.getDouble(GpsDirectory.TAG_GPS_LONGITUDE);
     560                    Double latitude = dirGps.getDouble(GpsDirectory.TAG_GPS_LATITUDE);
     561   
     562                    // Store values
     563   
     564                    e.setExifCoor(new LatLon(latitude, longitude));
     565                    e.setPos(e.getExifCoor());
     566                } catch (CompoundException ex) {
     567                    e.setExifCoor(null);
     568                    e.setPos(null);
     569                }
    600570            }
    601571        } catch (Exception ex) { // (other exceptions, e.g. #5271)
  • trunk/src/org/openstreetmap/josm/tools/ExifReader.java

    r5610 r6127  
    33
    44import java.io.File;
     5import java.io.IOException;
    56import java.text.ParseException;
    67import java.util.Date;
    7 import java.util.Iterator;
    88
    99import com.drew.imaging.jpeg.JpegMetadataReader;
     
    1313import com.drew.metadata.MetadataException;
    1414import com.drew.metadata.Tag;
    15 import com.drew.metadata.exif.ExifDirectory;
     15import com.drew.metadata.exif.ExifIFD0Directory;
     16import com.drew.metadata.exif.ExifSubIFDDirectory;
    1617
    1718/**
     
    2627            String dateStr = null;
    2728            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 */) {
    3232                        dateStr = tag.getDescription();
    3333                        break OUTER; // prefer this tag
    3434                    }
    35                     if (tag.getTagType() == ExifDirectory.TAG_DATETIME /* 0x0132 */ ||
    36                         tag.getTagType() == ExifDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */) {
     35                    if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */ ||
     36                        tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */) {
    3737                        dateStr = tag.getDescription();
    3838                    }
     
    5555        try {
    5656            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
    57             final Directory dir = metadata.getDirectory(ExifDirectory.class);
    58             orientation = dir.getInt(ExifDirectory.TAG_ORIENTATION);
     57            final Directory dir = metadata.getDirectory(ExifIFD0Directory.class);
     58            orientation = dir.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    5959        } catch (JpegProcessingException e) {
    6060            e.printStackTrace();
    6161        } catch (MetadataException e) {
     62            e.printStackTrace();
     63        } catch (IOException e) {
    6264            e.printStackTrace();
    6365        }
Note: See TracChangeset for help on using the changeset viewer.