Ticket #8895: upgrade-metadata-extractor3.patch

File upgrade-metadata-extractor3.patch, 970.1 KB (added by ebourg, 12 years ago)

Updated patch including the metadata-extractor source files

  • src/com/drew/imaging/ImageProcessingException.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.imaging;
     22
     23import com.drew.lang.CompoundException;
     24import com.drew.lang.annotations.Nullable;
     25
     26/**
     27 * An exception class thrown upon an unexpected condition that was fatal for the processing of an image.
     28 *
     29 * @author Drew Noakes http://drewnoakes.com
     30 */
     31public class ImageProcessingException extends CompoundException
     32{
     33    private static final long serialVersionUID = -9115669182209912676L;
     34
     35    public ImageProcessingException(@Nullable String message)
     36    {
     37        super(message);
     38    }
     39
     40    public ImageProcessingException(@Nullable String message, @Nullable Throwable cause)
     41    {
     42        super(message, cause);
     43    }
     44
     45    public ImageProcessingException(@Nullable Throwable cause)
     46    {
     47        super(cause);
     48    }
     49}
  • src/com/drew/imaging/ImageProcessingException.java

  • src/com/drew/imaging/jpeg/JpegMetadataReader.java

    Modification de propriétés sur src/com/drew/imaging/ImageProcessingException.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
    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
    2932import java.io.File;
    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{}
    4146//    public static Metadata readMetadata(IIOImage image) 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
    5157    {
     58        JpegSegmentReader segmentReader = new JpegSegmentReader(inputStream, waitForBytes);
     59        return extractMetadataFromJpegSegmentReader(segmentReader.getSegmentData());
     60    }
     61
     62    @NotNull
     63    public static Metadata readMetadata(@NotNull File file) throws JpegProcessingException, IOException
     64    {
    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?
    65         }
    6673
    67         try {
    68             byte[] iptcSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APPD);
    69             new IptcReader(iptcSegment).extract(metadata);
    70         } catch (JpegProcessingException e) {
    71             // TODO lodge error message within iptc directory?
     74        // Loop through looking for all SOFn segments.  When we find one, we know what type of compression
     75        // was used for the JPEG, and we can process the JPEG metadata in the segment too.
     76        for (byte i = 0; i < 16; i++) {
     77            // There are no SOF4 or SOF12 segments, so don't bother
     78            if (i == 4 || i == 12)
     79                continue;
     80            // Should never have more than one SOFn for a given 'n'.
     81            byte[] jpegSegment = segmentReader.getSegment((byte)(JpegSegmentReader.SEGMENT_SOF0 + i));
     82            if (jpegSegment == null)
     83                continue;
     84            JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class);
     85            directory.setInt(JpegDirectory.TAG_JPEG_COMPRESSION_TYPE, i);
     86            new JpegReader().extract(new ByteArrayReader(jpegSegment), metadata);
     87            break;
    7288        }
    7389
    74                 try {
    75                         byte[] jpegSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_SOF0);
    76                         new JpegReader(jpegSegment).extract(metadata);
    77                 } catch (JpegProcessingException e) {
    78                         // TODO lodge error message within jpeg directory?
    79                 }
     90        // There should never be more than one COM segment.
     91        byte[] comSegment = segmentReader.getSegment(JpegSegmentReader.SEGMENT_COM);
     92        if (comSegment != null)
     93            new JpegCommentReader().extract(new ByteArrayReader(comSegment), metadata);
    8094
    81                 try {
    82                         byte[] jpegCommentSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_COM);
    83                         new JpegCommentReader(jpegCommentSegment).extract(metadata);
    84                 } catch (JpegProcessingException e) {
    85                         // TODO lodge error message within jpegcomment directory?
    86                 }
     95        // Loop through all APP1 segments, checking the leading bytes to identify the format of each.
     96        for (byte[] app1Segment : segmentReader.getSegments(JpegSegmentReader.SEGMENT_APP1)) {
     97            if (app1Segment.length > 3 && "EXIF".equalsIgnoreCase(new String(app1Segment, 0, 4)))
     98                new ExifReader().extract(new ByteArrayReader(app1Segment), metadata);
    8799
    88         return metadata;
    89     }
    90 
    91     private JpegMetadataReader()
    92     {
    93     }
    94 
    95     public static void main(String[] args) throws MetadataException, IOException
    96     {
    97         Metadata metadata = null;
    98         try {
    99             metadata = JpegMetadataReader.readMetadata(new File(args[0]));
    100         } catch (Exception e) {
    101             e.printStackTrace(System.err);
    102             System.exit(1);
     100            //if (app1Segment.length > 27 && "http://ns.adobe.com/xap/1.0/".equalsIgnoreCase(new String(app1Segment, 0, 28)))
     101            //    new XmpReader().extract(new ByteArrayReader(app1Segment), metadata);
    103102        }
    104103
    105         // iterate over the exif data and print to System.out
    106         Iterator directories = metadata.getDirectoryIterator();
    107         while (directories.hasNext()) {
    108             Directory directory = (Directory)directories.next();
    109             Iterator tags = directory.getTagIterator();
    110             while (tags.hasNext()) {
    111                 Tag tag = (Tag)tags.next();
    112                 try {
    113                     System.out.println("[" + directory.getName() + "] " + tag.getTagName() + " = " + tag.getDescription());
    114                 } catch (MetadataException e) {
    115                     System.err.println(e.getMessage());
    116                     System.err.println(tag.getDirectoryName() + " " + tag.getTagName() + " (error)");
    117                 }
     104        // Loop through all APPD segments, checking the leading bytes to identify the format of each.
     105        for (byte[] appdSegment : segmentReader.getSegments(JpegSegmentReader.SEGMENT_APPD)) {
     106            if (appdSegment.length > 12 && "Photoshop 3.0".compareTo(new String(appdSegment, 0, 13))==0) {
     107                //new PhotoshopReader().extract(new ByteArrayReader(appdSegment), metadata);
     108            } else {
     109                // TODO might be able to check for a leading 0x1c02 for IPTC data...
     110                new IptcReader().extract(new ByteArrayReader(appdSegment), metadata);
    118111            }
    119             if (directory.hasErrors()) {
    120                 Iterator errors = directory.getErrors();
    121                 while (errors.hasNext()) {
    122                     System.out.println("ERROR: " + errors.next());
    123                 }
    124             }
    125112        }
    126113
    127         if (args.length>1 && args[1].trim().equals("/thumb"))
    128         {
    129             ExifDirectory directory = (ExifDirectory)metadata.getDirectory(ExifDirectory.class);
    130             if (directory.containsThumbnail())
    131             {
    132                 System.out.println("Writing thumbnail...");
    133                 directory.writeThumbnail(args[0].trim() + ".thumb.jpg");
    134             }
    135             else
    136             {
    137                 System.out.println("No thumbnail data exists in this image");
    138             }
    139         }
     114        return metadata;
    140115    }
     116
     117    private JpegMetadataReader() throws Exception
     118    {
     119        throw new Exception("Not intended for instantiation");
     120    }
    141121}
     122
  • src/com/drew/imaging/jpeg/JpegProcessingException.java

     
    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);
    4348    }
  • src/com/drew/imaging/jpeg/JpegSegmentData.java

     
    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.jpeg;
    1622
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25
    1726import java.io.*;
    1827import java.util.ArrayList;
    1928import java.util.HashMap;
     
    2130
    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;
     46    @NotNull
     47    private final HashMap<Byte, List<byte[]>> _segmentDataMap = new HashMap<Byte, List<byte[]>>(10);
    3348
    34     public JpegSegmentData()
     49    /**
     50     * Adds segment bytes to the collection.
     51     * @param segmentMarker
     52     * @param segmentBytes
     53     */
     54    @SuppressWarnings({ "MismatchedQueryAndUpdateOfCollection" })
     55    public void addSegment(byte segmentMarker, @NotNull byte[] segmentBytes)
    3556    {
    36         _segmentDataMap = new HashMap(10);
    37     }
    38 
    39     public void addSegment(byte segmentMarker, byte[] segmentBytes)
    40     {
    41         List segmentList = getOrCreateSegmentList(segmentMarker);
     57        final List<byte[]> segmentList = getOrCreateSegmentList(segmentMarker);
    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    {
    4769        return getSegment(segmentMarker, 0);
    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);
     87            return segmentList.get(occurrence);
    5888    }
    5989
    60     public int getSegmentCount(byte segmentMarker)
     90    /**
     91     * Returns all instances of a given Jpeg segment.  If no instances exist, an empty sequence is returned.
     92     *
     93     * @param segmentMarker a number which identifies the type of Jpeg segment being queried
     94     * @return zero or more byte arrays, each holding the data of a Jpeg segment
     95     */
     96    @NotNull
     97    public Iterable<byte[]> getSegments(byte segmentMarker)
    6198    {
    62         final List segmentList = getSegmentList(segmentMarker);
    63         if (segmentList==null)
    64             return 0;
    65         else
    66             return segmentList.size();
     99        final List<byte[]> segmentList = getSegmentList(segmentMarker);
     100        return segmentList==null ? new ArrayList<byte[]>() : segmentList;
    67101    }
    68102
    69     public void removeSegmentOccurrence(byte segmentMarker, int occurrence)
     103    @Nullable
     104    public List<byte[]> getSegmentList(byte segmentMarker)
    70105    {
    71         final List segmentList = (List)_segmentDataMap.get(new Byte(segmentMarker));
    72         segmentList.remove(occurrence);
     106        return _segmentDataMap.get(Byte.valueOf(segmentMarker));
    73107    }
    74108
    75     public void removeSegment(byte segmentMarker)
     109    @NotNull
     110    private List<byte[]> getOrCreateSegmentList(byte segmentMarker)
    76111    {
    77         _segmentDataMap.remove(new Byte(segmentMarker));
     112        List<byte[]> segmentList;
     113        if (_segmentDataMap.containsKey(segmentMarker)) {
     114            segmentList = _segmentDataMap.get(segmentMarker);
     115        } else {
     116            segmentList = new ArrayList<byte[]>();
     117            _segmentDataMap.put(segmentMarker, segmentList);
     118        }
     119        return segmentList;
    78120    }
    79121
    80     private List getSegmentList(byte segmentMarker)
     122    /**
     123     * Returns the count of segment data byte arrays stored for a given segment marker.
     124     * @param segmentMarker identifies the required segment
     125     * @return the segment count (zero if no segments exist).
     126     */
     127    public int getSegmentCount(byte segmentMarker)
    81128    {
    82         return (List)_segmentDataMap.get(new Byte(segmentMarker));
     129        final List<byte[]> segmentList = getSegmentList(segmentMarker);
     130        return segmentList == null ? 0 : segmentList.size();
    83131    }
    84132
    85     private List getOrCreateSegmentList(byte segmentMarker)
     133    /**
     134     * Removes a specified instance of a segment's data from the collection.  Use this method when more than one
     135     * occurrence of segment data for a given marker exists.
     136     * @param segmentMarker identifies the required segment
     137     * @param occurrence the zero-based index of the segment occurrence to remove.
     138     */
     139    @SuppressWarnings({ "MismatchedQueryAndUpdateOfCollection" })
     140    public void removeSegmentOccurrence(byte segmentMarker, int occurrence)
    86141    {
    87         List segmentList;
    88         Byte key = new Byte(segmentMarker);
    89         if (_segmentDataMap.containsKey(key)) {
    90             segmentList = (List)_segmentDataMap.get(key);
    91         } else {
    92             segmentList = new ArrayList();
    93             _segmentDataMap.put(key, segmentList);
    94         }
    95         return segmentList;
     142        final List<byte[]> segmentList = _segmentDataMap.get(Byte.valueOf(segmentMarker));
     143        segmentList.remove(occurrence);
    96144    }
    97145
     146    /**
     147     * Removes all segments from the collection having the specified marker.
     148     * @param segmentMarker identifies the required segment
     149     */
     150    public void removeSegment(byte segmentMarker)
     151    {
     152        _segmentDataMap.remove(Byte.valueOf(segmentMarker));
     153    }
     154
     155    /**
     156     * Determines whether data is present for a given segment marker.
     157     * @param segmentMarker identifies the required segment
     158     * @return true if data exists, otherwise false
     159     */
    98160    public boolean containsSegment(byte segmentMarker)
    99161    {
    100         return _segmentDataMap.containsKey(new Byte(segmentMarker));
     162        return _segmentDataMap.containsKey(Byte.valueOf(segmentMarker));
    101163    }
    102164
    103     public static void ToFile(File file, JpegSegmentData segmentData) throws IOException
     165    /**
     166     * Serialises the contents of a JpegSegmentData to a file.
     167     * @param file to file to write from
     168     * @param segmentData the data to write
     169     * @throws IOException if problems occur while writing
     170     */
     171    public static void toFile(@NotNull File file, @NotNull JpegSegmentData segmentData) throws IOException
    104172    {
    105         ObjectOutputStream outputStream = null;
     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();
     181            if (fileOutputStream!=null)
     182                fileOutputStream.close();
    115183        }
    116184    }
    117185
    118     public static JpegSegmentData FromFile(File file) throws IOException, ClassNotFoundException
     186    /**
     187     * Deserialises the contents of a JpegSegmentData from a file.
     188     * @param file the file to read from
     189     * @return the JpegSegmentData as read
     190     * @throws IOException if problems occur while reading
     191     * @throws ClassNotFoundException if problems occur while deserialising
     192     */
     193    @NotNull
     194    public static JpegSegmentData fromFile(@NotNull File file) throws IOException, ClassNotFoundException
    119195    {
    120196        ObjectInputStream inputStream = null;
    121197        try
  • src/com/drew/imaging/jpeg/JpegSegmentReader.java

     
    11/*
    2  * JpegSegmentReader.java
     2 * Copyright 2002-2012 Drew Noakes
    33 *
    4  * This class written by Drew Noakes, in accordance with the Jpeg specification.
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
    57 *
    6  * This is public domain software - that is, you can do whatever you want
    7  * with it, and include it software that is licensed under the GNU or the
    8  * BSD license, or whatever other licence you choose, including proprietary
    9  * closed source licenses.  I do ask that you leave this header in tact.
     8 *        http://www.apache.org/licenses/LICENSE-2.0
    109 *
    11  * If you make modifications to this code that you think would benefit the
    12  * wider community, please send me a copy and I'll post it on my site.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
    1315 *
    14  * If you make use of this code, I'd appreciate hearing about it.
    15  *   drew@drewnoakes.com
    16  * Latest version of this software kept at
    17  *   http://drewnoakes.com/
     16 * More information about this project is available at:
    1817 *
    19  * Created by dnoakes on 04-Nov-2002 00:54:00 using IntelliJ IDEA
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    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
     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'
    3537
    36     /** Jpeg file */
    37     private final File _file;
    38     /** Jpeg data as byte array */
    39     private final byte[] _data;
    40     /** Jpeg data as an InputStream */
    41     private final InputStream _stream;
     38    @NotNull
     39    private final JpegSegmentData _segmentData;
    4240
    43     private JpegSegmentData _segmentData;
    44 
    4541    /**
    46      * Private, because this segment crashes my algorithm, and searching for
    47      * it doesn't work (yet).
     42     * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet).
    4843     */
    4944    private static final byte SEGMENT_SOS = (byte)0xDA;
    5045
     
    5348     */
    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. */
    6156    public static final byte SEGMENT_APP2 = (byte)0xE2;
     
    7368    public static final byte SEGMENT_APP8 = (byte)0xE8;
    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. */
    8984    public static final byte SEGMENT_SOI = (byte)0xD8;
     
    10095     * Creates a JpegSegmentReader for a specific file.
    10196     * @param file the Jpeg file to read segments from
    10297     */
    103     public JpegSegmentReader(File file) throws JpegProcessingException
     98    @SuppressWarnings({ "ConstantConditions" })
     99    public JpegSegmentReader(@NotNull File file) throws JpegProcessingException, IOException
    104100    {
    105         _file = file;
    106         _data = null;
    107         _stream = null;
     101        if (file==null)
     102            throw new NullPointerException();
    108103
    109         readSegments();
     104        InputStream inputStream = null;
     105        try {
     106            inputStream = new FileInputStream(file);
     107            _segmentData = readSegments(new BufferedInputStream(inputStream), false);
     108        } finally {
     109            if (inputStream != null)
     110                inputStream.close();
     111        }
    110112    }
    111113
    112114    /**
    113115     * Creates a JpegSegmentReader for a byte array.
    114116     * @param fileContents the byte array containing Jpeg data
    115117     */
    116     public JpegSegmentReader(byte[] fileContents) throws JpegProcessingException
     118    @SuppressWarnings({ "ConstantConditions" })
     119    public JpegSegmentReader(@NotNull byte[] fileContents) throws JpegProcessingException
    117120    {
    118         _file = null;
    119         _data = fileContents;
    120         _stream = null;
     121        if (fileContents==null)
     122            throw new NullPointerException();
    121123
    122         readSegments();
     124        BufferedInputStream stream = new BufferedInputStream(new ByteArrayInputStream(fileContents));
     125        _segmentData = readSegments(stream, false);
    123126    }
    124127
    125     public JpegSegmentReader(InputStream in) throws JpegProcessingException
     128    /**
     129     * Creates a JpegSegmentReader for an InputStream.
     130     * @param inputStream the InputStream containing Jpeg data
     131     */
     132    @SuppressWarnings({ "ConstantConditions" })
     133    public JpegSegmentReader(@NotNull InputStream inputStream, boolean waitForBytes) throws JpegProcessingException
    126134    {
    127         _stream = in;
    128         _file = null;
    129         _data = null;
    130        
    131         readSegments();
    132     }
     135        if (inputStream==null)
     136            throw new NullPointerException();
    133137
    134     public JpegSegmentReader(JpegSegmentData segmentData)
    135     {
    136         _file = null;
    137         _data = null;
    138         _stream = null;
     138        BufferedInputStream bufferedInputStream = inputStream instanceof BufferedInputStream
     139                ? (BufferedInputStream)inputStream
     140                : new BufferedInputStream(inputStream);
    139141
    140         _segmentData = segmentData;
     142        _segmentData = readSegments(bufferedInputStream, waitForBytes);
    141143    }
    142144
    143145    /**
     
    145147     * a byte array.
    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
    150150     */
    151     public byte[] readSegment(byte segmentMarker) throws JpegProcessingException
     151    @Nullable
     152    public byte[] readSegment(byte segmentMarker)
    152153    {
    153154        return readSegment(segmentMarker, 0);
    154155    }
    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    {
    165167        return _segmentData.getSegment(segmentMarker, occurrence);
    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    {
    170189        return _segmentData.getSegmentCount(segmentMarker);
    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    {
    175199        return _segmentData;
    176200    }
    177201
    178     private void readSegments() throws JpegProcessingException
     202    @NotNull
     203    private JpegSegmentData readSegments(@NotNull final BufferedInputStream jpegInputStream, boolean waitForBytes) throws JpegProcessingException
    179204    {
    180         _segmentData = new JpegSegmentData();
     205        JpegSegmentData segmentData = new JpegSegmentData();
    181206
    182         BufferedInputStream inStream = getJpegInputStream();
    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));
    195227                }
    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)) {
    215249                    // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would
    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            }
    239270        }
    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
     273    private boolean checkForBytesOnStream(@NotNull BufferedInputStream stream, int bytesNeeded, boolean waitForBytes) throws IOException
    249274    {
    250         if (_stream!=null) {
    251             if (_stream instanceof BufferedInputStream) {
    252                 return (BufferedInputStream) _stream;
    253             } else {
    254                 return new BufferedInputStream(_stream);
    255             }
    256         }
    257         InputStream inputStream;
    258         if (_data == null) {
     275        // NOTE  waiting is essential for network streams where data can be delayed, but it is not necessary for byte[] or filesystems
     276
     277        if (!waitForBytes)
     278            return bytesNeeded <= stream.available();
     279
     280        int count = 40; // * 100ms = approx 4 seconds
     281        while (count > 0) {
     282            if (bytesNeeded <= stream.available())
     283               return true;
    259284            try {
    260                 inputStream = new FileInputStream(_file);
    261             } catch (FileNotFoundException e) {
    262                 throw new JpegProcessingException("Jpeg file does not exist", e);
     285                Thread.sleep(100);
     286            } catch (InterruptedException e) {
     287                // continue
    263288            }
    264         } else {
    265             inputStream = new ByteArrayInputStream(_data);
     289            count--;
    266290        }
    267         return new BufferedInputStream(inputStream);
     291        return false;
    268292    }
    269 
    270     /**
    271      * Helper method that validates the Jpeg file's magic number.
    272      * @param fileStream the InputStream to read bytes from, which must be positioned
    273      *        at its start (i.e. no bytes read yet)
    274      * @return true if the magic number is Jpeg (0xFFD8)
    275      * @throws IOException for any problem in reading the file
    276      */
    277     private boolean isValidJpegHeaderBytes(InputStream fileStream) throws IOException
    278     {
    279         byte[] header = new byte[2];
    280         fileStream.read(header, 0, 2);
    281         return (header[0] & 0xFF) == 0xFF && (header[1] & 0xFF) == 0xD8;
    282     }
    283 }
    284  Pas de retour chariot à la fin du fichier
     293}
  • src/com/drew/imaging/PhotographicConversions.java

     
    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;
    1622
    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
    3142     */
    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    }
    4150
    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
    4656     */
    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}
  • src/com/drew/lang/annotations/NotNull.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang.annotations;
     23
     24/**
     25 * @author Drew Noakes http://drewnoakes.com
     26 */
     27public @interface NotNull
     28{
     29}
  • src/com/drew/lang/annotations/NotNull.java

  • src/com/drew/lang/annotations/Nullable.java

    Modification de propriétés sur src/com/drew/lang/annotations/NotNull.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang.annotations;
     23
     24/**
     25 * @author Drew Noakes http://drewnoakes.com
     26 */
     27public @interface Nullable
     28{
     29}
  • src/com/drew/lang/annotations/Nullable.java

  • src/com/drew/lang/annotations/SuppressWarnings.java

    Modification de propriétés sur src/com/drew/lang/annotations/Nullable.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2011 Andreas Ziermann
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang.annotations;
     23
     24/**
     25 * Used to suppress specific code analysis warnings produced by the Findbugs tool.
     26 *
     27 * @author Andreas Ziermann
     28 */
     29public @interface SuppressWarnings
     30{
     31    /**
     32     * The name of the warning to be suppressed.
     33     * @return The name of the warning to be suppressed.
     34     */
     35    @NotNull String value();
     36
     37    /**
     38     * An explanation of why it is valid to suppress the warning in a particular situation/context.
     39     * @return An explanation of why it is valid to suppress the warning in a particular situation/context.
     40     */
     41    @NotNull String justification();
     42}
  • src/com/drew/lang/annotations/SuppressWarnings.java

  • src/com/drew/lang/BufferBoundsException.java

    Modification de propriétés sur src/com/drew/lang/annotations/SuppressWarnings.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang;
     23
     24import com.drew.lang.annotations.NotNull;
     25
     26import java.io.IOException;
     27
     28/**
     29 * A checked replacement for IndexOutOfBoundsException.  Used by BufferReader.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     33public final class BufferBoundsException extends Exception
     34{
     35    private static final long serialVersionUID = 2911102837808946396L;
     36
     37    public BufferBoundsException(@NotNull byte[] buffer, int index, int bytesRequested)
     38    {
     39        super(getMessage(buffer, index, bytesRequested));
     40    }
     41
     42    public BufferBoundsException(final String message)
     43    {
     44        super(message);
     45    }
     46
     47    public BufferBoundsException(final String message, final IOException innerException)
     48    {
     49        super(message, innerException);
     50    }
     51
     52    private static String getMessage(@NotNull byte[] buffer, int index, int bytesRequested)
     53    {
     54        if (index < 0)
     55            return String.format("Attempt to read from buffer using a negative index (%s)", index);
     56
     57        return String.format("Attempt to read %d byte%s from beyond end of buffer (requested index: %d, max index: %d)",
     58                bytesRequested, bytesRequested==1?"":"s", index, buffer.length - 1);
     59    }
     60}
  • src/com/drew/lang/BufferBoundsException.java

  • src/com/drew/lang/BufferReader.java

    Modification de propriétés sur src/com/drew/lang/BufferBoundsException.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang;
     23
     24import com.drew.lang.annotations.NotNull;
     25
     26public interface BufferReader
     27{
     28    /**
     29     * Returns the length of the buffer.  This value represents the total number of bytes in the underlying source.
     30     *
     31     * @return The number of bytes in the buffer.
     32     */
     33    long getLength();
     34
     35    /**
     36     * Sets the endianness of this reader.
     37     * <ul>
     38     * <li><code>true</code> for Motorola (or big) endianness</li>
     39     * <li><code>false</code> for Intel (or little) endianness</li>
     40     * </ul>
     41     *
     42     * @param motorolaByteOrder <code>true</code> for motorola/big endian, <code>false</code> for intel/little endian
     43     */
     44    void setMotorolaByteOrder(boolean motorolaByteOrder);
     45
     46    /**
     47     * Gets the endianness of this reader.
     48     * <ul>
     49     * <li><code>true</code> for Motorola (or big) endianness</li>
     50     * <li><code>false</code> for Intel (or little) endianness</li>
     51     * </ul>
     52     */
     53    boolean isMotorolaByteOrder();
     54
     55    /**
     56     * Returns an unsigned 8-bit int calculated from one byte of data at the specified index.
     57     *
     58     * @param index position within the data buffer to read byte
     59     * @return the 8 bit int value, between 0 and 255
     60     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     61     */
     62    short getUInt8(int index) throws BufferBoundsException;
     63
     64    /**
     65     * Returns a signed 8-bit int calculated from one byte of data at the specified index.
     66     *
     67     * @param index position within the data buffer to read byte
     68     * @return the 8 bit int value, between 0x00 and 0xFF
     69     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     70     */
     71    byte getInt8(int index) throws BufferBoundsException;
     72
     73    /**
     74     * Returns an unsigned 16-bit int calculated from two bytes of data at the specified index.
     75     *
     76     * @param index position within the data buffer to read first byte
     77     * @return the 16 bit int value, between 0x0000 and 0xFFFF
     78     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     79     */
     80    int getUInt16(int index) throws BufferBoundsException;
     81
     82    /**
     83     * Returns a signed 16-bit int calculated from two bytes of data at the specified index (MSB, LSB).
     84     *
     85     * @param index position within the data buffer to read first byte
     86     * @return the 16 bit int value, between 0x0000 and 0xFFFF
     87     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     88     */
     89    short getInt16(int index) throws BufferBoundsException;
     90
     91    /**
     92     * Get a 32-bit unsigned integer from the buffer, returning it as a long.
     93     *
     94     * @param index position within the data buffer to read first byte
     95     * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF
     96     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     97     */
     98    long getUInt32(int index) throws BufferBoundsException;
     99
     100    /**
     101     * Returns a signed 32-bit integer from four bytes of data at the specified index the buffer.
     102     *
     103     * @param index position within the data buffer to read first byte
     104     * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF
     105     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     106     */
     107    int getInt32(int index) throws BufferBoundsException;
     108
     109    /**
     110     * Get a signed 64-bit integer from the buffer.
     111     *
     112     * @param index position within the data buffer to read first byte
     113     * @return the 64 bit int value, between 0x0000000000000000 and 0xFFFFFFFFFFFFFFFF
     114     * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative
     115     */
     116    long getInt64(int index) throws BufferBoundsException;
     117
     118    float getS15Fixed16(int index) throws BufferBoundsException;
     119
     120    float getFloat32(int index) throws BufferBoundsException;
     121
     122    double getDouble64(int index) throws BufferBoundsException;
     123
     124    @NotNull
     125    byte[] getBytes(int index, int count) throws BufferBoundsException;
     126
     127    @NotNull
     128    String getString(int index, int bytesRequested) throws BufferBoundsException;
     129
     130    @NotNull
     131    String getString(int index, int bytesRequested, String charset) throws BufferBoundsException;
     132
     133    /**
     134     * Creates a String from the _data buffer starting at the specified index,
     135     * and ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>.
     136     *
     137     * @param index          The index within the buffer at which to start reading the string.
     138     * @param maxLengthBytes The maximum number of bytes to read.  If a zero-byte is not reached within this limit,
     139     *                       reading will stop and the string will be truncated to this length.
     140     * @return The read string.
     141     * @throws BufferBoundsException The buffer does not contain enough bytes to satisfy this request.
     142     */
     143    @NotNull
     144    String getNullTerminatedString(int index, int maxLengthBytes) throws BufferBoundsException;
     145}
  • src/com/drew/lang/BufferReader.java

  • src/com/drew/lang/ByteArrayReader.java

    Modification de propriétés sur src/com/drew/lang/BufferReader.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang;
     23
     24import com.drew.lang.annotations.NotNull;
     25
     26import java.io.UnsupportedEncodingException;
     27
     28/**
     29 * Provides methods to read specific values from a byte array, with a consistent, checked exception structure for
     30 * issues.
     31 * <p/>
     32 * By default, the reader operates with Motorola byte order (big endianness).  This can be changed by calling
     33 * {@see setMotorolaByteOrder(boolean)}.
     34 *
     35 * @author Drew Noakes http://drewnoakes.com
     36 * */
     37public class ByteArrayReader implements BufferReader
     38{
     39    @NotNull
     40    private final byte[] _buffer;
     41    private boolean _isMotorolaByteOrder = true;
     42
     43    @SuppressWarnings({ "ConstantConditions" })
     44    @com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
     45    public ByteArrayReader(@NotNull byte[] buffer)
     46    {
     47        if (buffer == null)
     48            throw new NullPointerException();
     49       
     50        _buffer = buffer;
     51    }
     52
     53    @Override
     54    public long getLength()
     55    {
     56        return _buffer.length;
     57    }
     58
     59
     60    @Override
     61    public void setMotorolaByteOrder(boolean motorolaByteOrder)
     62    {
     63        _isMotorolaByteOrder = motorolaByteOrder;
     64    }
     65
     66    @Override
     67    public boolean isMotorolaByteOrder()
     68    {
     69        return _isMotorolaByteOrder;
     70    }
     71
     72    @Override
     73    public short getUInt8(int index) throws BufferBoundsException
     74    {
     75        checkBounds(index, 1);
     76
     77        return (short) (_buffer[index] & 255);
     78    }
     79
     80    @Override
     81    public byte getInt8(int index) throws BufferBoundsException
     82    {
     83        checkBounds(index, 1);
     84
     85        return _buffer[index];
     86    }
     87
     88    @Override
     89    public int getUInt16(int index) throws BufferBoundsException
     90    {
     91        checkBounds(index, 2);
     92
     93        if (_isMotorolaByteOrder) {
     94            // Motorola - MSB first
     95            return (_buffer[index    ] << 8 & 0xFF00) |
     96                   (_buffer[index + 1]      & 0xFF);
     97        } else {
     98            // Intel ordering - LSB first
     99            return (_buffer[index + 1] << 8 & 0xFF00) |
     100                   (_buffer[index    ]      & 0xFF);
     101        }
     102    }
     103
     104    @Override
     105    public short getInt16(int index) throws BufferBoundsException
     106    {
     107        checkBounds(index, 2);
     108
     109        if (_isMotorolaByteOrder) {
     110            // Motorola - MSB first
     111            return (short) (((short)_buffer[index    ] << 8 & (short)0xFF00) |
     112                            ((short)_buffer[index + 1]      & (short)0xFF));
     113        } else {
     114            // Intel ordering - LSB first
     115            return (short) (((short)_buffer[index + 1] << 8 & (short)0xFF00) |
     116                            ((short)_buffer[index    ]      & (short)0xFF));
     117        }
     118    }
     119
     120    @Override
     121    public long getUInt32(int index) throws BufferBoundsException
     122    {
     123        checkBounds(index, 4);
     124
     125        if (_isMotorolaByteOrder) {
     126            // Motorola - MSB first (big endian)
     127            return (((long)_buffer[index    ]) << 24 & 0xFF000000L) |
     128                    (((long)_buffer[index + 1]) << 16 & 0xFF0000L) |
     129                    (((long)_buffer[index + 2]) << 8  & 0xFF00L) |
     130                    (((long)_buffer[index + 3])       & 0xFFL);
     131        } else {
     132            // Intel ordering - LSB first (little endian)
     133            return (((long)_buffer[index + 3]) << 24 & 0xFF000000L) |
     134                    (((long)_buffer[index + 2]) << 16 & 0xFF0000L) |
     135                    (((long)_buffer[index + 1]) << 8  & 0xFF00L) |
     136                    (((long)_buffer[index    ])       & 0xFFL);
     137        }
     138    }
     139
     140    @Override
     141    public int getInt32(int index) throws BufferBoundsException
     142    {
     143        checkBounds(index, 4);
     144
     145        if (_isMotorolaByteOrder) {
     146            // Motorola - MSB first (big endian)
     147            return (_buffer[index    ] << 24 & 0xFF000000) |
     148                   (_buffer[index + 1] << 16 & 0xFF0000) |
     149                   (_buffer[index + 2] << 8  & 0xFF00) |
     150                   (_buffer[index + 3]       & 0xFF);
     151        } else {
     152            // Intel ordering - LSB first (little endian)
     153            return (_buffer[index + 3] << 24 & 0xFF000000) |
     154                   (_buffer[index + 2] << 16 & 0xFF0000) |
     155                   (_buffer[index + 1] << 8  & 0xFF00) |
     156                   (_buffer[index    ]       & 0xFF);
     157        }
     158    }
     159
     160    @Override
     161    public long getInt64(int index) throws BufferBoundsException
     162    {
     163        checkBounds(index, 8);
     164
     165        if (_isMotorolaByteOrder) {
     166            // Motorola - MSB first
     167            return ((long)_buffer[index    ] << 56 & 0xFF00000000000000L) |
     168                   ((long)_buffer[index + 1] << 48 & 0xFF000000000000L) |
     169                   ((long)_buffer[index + 2] << 40 & 0xFF0000000000L) |
     170                   ((long)_buffer[index + 3] << 32 & 0xFF00000000L) |
     171                   ((long)_buffer[index + 4] << 24 & 0xFF000000L) |
     172                   ((long)_buffer[index + 5] << 16 & 0xFF0000L) |
     173                   ((long)_buffer[index + 6] << 8  & 0xFF00L) |
     174                   ((long)_buffer[index + 7]       & 0xFFL);
     175        } else {
     176            // Intel ordering - LSB first
     177            return ((long)_buffer[index + 7] << 56 & 0xFF00000000000000L) |
     178                   ((long)_buffer[index + 6] << 48 & 0xFF000000000000L) |
     179                   ((long)_buffer[index + 5] << 40 & 0xFF0000000000L) |
     180                   ((long)_buffer[index + 4] << 32 & 0xFF00000000L) |
     181                   ((long)_buffer[index + 3] << 24 & 0xFF000000L) |
     182                   ((long)_buffer[index + 2] << 16 & 0xFF0000L) |
     183                   ((long)_buffer[index + 1] << 8  & 0xFF00L) |
     184                   ((long)_buffer[index    ]       & 0xFFL);
     185        }
     186    }
     187
     188    @Override
     189    public float getS15Fixed16(int index) throws BufferBoundsException
     190    {
     191        checkBounds(index, 4);
     192
     193        if (_isMotorolaByteOrder) {
     194            float res = (_buffer[index    ] & 255) << 8 |
     195                        (_buffer[index + 1] & 255);
     196            int d =     (_buffer[index + 2] & 255) << 8 |
     197                        (_buffer[index + 3] & 255);
     198            return (float)(res + d/65536.0);
     199        } else {
     200            // this particular branch is untested
     201            float res = (_buffer[index + 3] & 255) << 8 |
     202                        (_buffer[index + 2] & 255);
     203            int d =     (_buffer[index + 1] & 255) << 8 |
     204                        (_buffer[index    ] & 255);
     205            return (float)(res + d/65536.0);
     206        }
     207    }
     208
     209    @Override
     210    public float getFloat32(int index) throws BufferBoundsException
     211    {
     212        return Float.intBitsToFloat(getInt32(index));
     213    }
     214
     215    @Override
     216    public double getDouble64(int index) throws BufferBoundsException
     217    {
     218        return Double.longBitsToDouble(getInt64(index));
     219    }
     220   
     221    @Override
     222    @NotNull
     223    public byte[] getBytes(int index, int count) throws BufferBoundsException
     224    {
     225        checkBounds(index, count);
     226
     227        byte[] bytes = new byte[count];
     228        System.arraycopy(_buffer, index, bytes, 0, count);
     229        return bytes;
     230    }
     231
     232    @Override
     233    @NotNull
     234    public String getString(int index, int bytesRequested) throws BufferBoundsException
     235    {
     236        return new String(getBytes(index, bytesRequested));
     237    }
     238
     239    @Override
     240    @NotNull
     241    public String getString(int index, int bytesRequested, String charset) throws BufferBoundsException
     242    {
     243        byte[] bytes = getBytes(index, bytesRequested);
     244        try {
     245            return new String(bytes, charset);
     246        } catch (UnsupportedEncodingException e) {
     247            return new String(bytes);
     248        }
     249    }
     250
     251    @Override
     252    @NotNull
     253    public String getNullTerminatedString(int index, int maxLengthBytes) throws BufferBoundsException
     254    {
     255        // NOTE currently only really suited to single-byte character strings
     256
     257        checkBounds(index, maxLengthBytes);
     258
     259        // Check for null terminators
     260        int length = 0;
     261        while ((index + length) < _buffer.length && _buffer[index + length] != '\0' && length < maxLengthBytes)
     262            length++;
     263
     264        byte[] bytes = getBytes(index, length);
     265        return new String(bytes);
     266    }
     267
     268    private void checkBounds(final int index, final int bytesRequested) throws BufferBoundsException
     269    {
     270        if (bytesRequested < 0 || index < 0 || (long)index + (long)bytesRequested - 1L >= (long)_buffer.length)
     271            throw new BufferBoundsException(_buffer, index, bytesRequested);
     272    }
     273}
  • src/com/drew/lang/ByteArrayReader.java

  • src/com/drew/lang/CompoundException.java

    Modification de propriétés sur src/com/drew/lang/ByteArrayReader.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
    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;
    1622
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25
    1726import java.io.PrintStream;
    1827import java.io.PrintWriter;
    1928
     
    2130 * Represents a compound exception, as modelled in JDK 1.4, but
    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    }
    8096
    8197    public void printStackTrace()
    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    }
    89105}
  • src/com/drew/lang/GeoLocation.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26
     27/**
     28 * Represents a latitude and longitude pair, giving a position on earth in spherical coordinates.
     29 * Values of latitude and longitude are given in degrees.
     30 * This type is immutable.
     31 */
     32public final class GeoLocation
     33{
     34    private final double _latitude;
     35    private final double _longitude;
     36
     37    /**
     38     * Instantiates a new instance of {@link GeoLocation}.
     39     *
     40     * @param latitude the latitude, in degrees
     41     * @param longitude the longitude, in degrees
     42     */
     43    public GeoLocation(double latitude, double longitude)
     44    {
     45        _latitude = latitude;
     46        _longitude = longitude;
     47    }
     48
     49    /**
     50     * @return the latitudinal angle of this location, in degrees.
     51     */
     52    public double getLatitude()
     53    {
     54        return _latitude;
     55    }
     56
     57    /**
     58     * @return the longitudinal angle of this location, in degrees.
     59     */
     60    public double getLongitude()
     61    {
     62        return _longitude;
     63    }
     64
     65    /**
     66     * @return true, if both latitude and longitude are equal to zero
     67     */
     68    public boolean isZero()
     69    {
     70        return _latitude == 0 && _longitude == 0;
     71    }
     72
     73    /**
     74     * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) representation as a string,
     75     * of format: {@code -1° 23' 4.56"}
     76     */
     77    @NotNull
     78    public static String decimalToDegreesMinutesSecondsString(double decimal)
     79    {
     80        double[] dms = decimalToDegreesMinutesSeconds(decimal);
     81        return dms[0] + "° " + dms[1] + "' " + dms[2] + '"';
     82    }
     83
     84    /**
     85     * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) component values, as
     86     * a double array.
     87     */
     88    @NotNull
     89    public static double[] decimalToDegreesMinutesSeconds(double decimal)
     90    {
     91        int d = (int)decimal;
     92        double m = Math.abs((decimal % 1) * 60);
     93        double s = (m % 1) * 60;
     94        return new double[] { d, (int)m, s};
     95    }
     96
     97    /**
     98     * Converts DMS (degrees-minutes-seconds) rational values, as given in {@link com.drew.metadata.exif.GpsDirectory},
     99     * into a single value in degrees, as a double.
     100     */
     101    @Nullable
     102    public static Double degreesMinutesSecondsToDecimal(@NotNull final Rational degs, @NotNull final Rational mins, @NotNull final Rational secs, final boolean isNegative)
     103    {
     104        double decimal = Math.abs(degs.doubleValue())
     105                + mins.doubleValue() / 60.0d
     106                + secs.doubleValue() / 3600.0d;
     107
     108        if (Double.isNaN(decimal))
     109            return null;
     110
     111        if (isNegative)
     112            decimal *= -1;
     113
     114        return decimal;
     115    }
     116
     117    @Override
     118    public boolean equals(final Object o)
     119    {
     120        if (this == o) return true;
     121        if (o == null || getClass() != o.getClass()) return false;
     122        GeoLocation that = (GeoLocation) o;
     123        if (Double.compare(that._latitude, _latitude) != 0) return false;
     124        if (Double.compare(that._longitude, _longitude) != 0) return false;
     125        return true;
     126    }
     127
     128    @Override
     129    public int hashCode()
     130    {
     131        int result;
     132        long temp;
     133        temp = _latitude != +0.0d ? Double.doubleToLongBits(_latitude) : 0L;
     134        result = (int) (temp ^ (temp >>> 32));
     135        temp = _longitude != +0.0d ? Double.doubleToLongBits(_longitude) : 0L;
     136        result = 31 * result + (int) (temp ^ (temp >>> 32));
     137        return result;
     138    }
     139
     140    /**
     141     * @return a string representation of this location, of format: {@code 1.23, 4.56}
     142     */
     143    @Override
     144    @NotNull
     145    public String toString()
     146    {
     147        return _latitude + ", " + _longitude;
     148    }
     149
     150    /**
     151     * @return a string representation of this location, of format: {@code -1° 23' 4.56", 54° 32' 1.92"}
     152     */
     153    @NotNull
     154    public String toDMSString()
     155    {
     156        return decimalToDegreesMinutesSecondsString(_latitude) + ", " + decimalToDegreesMinutesSecondsString(_longitude);
     157    }
     158}
  • src/com/drew/lang/GeoLocation.java

  • src/com/drew/lang/NullOutputStream.java

    Modification de propriétés sur src/com/drew/lang/GeoLocation.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
    1 /**
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
     1/*
     2 * Copyright 2002-2012 Drew Noakes
    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;
    1822
    1923import java.io.IOException;
    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{
    2433    public NullOutputStream()
  • src/com/drew/lang/Rational.java

     
    11/*
    2  * Rational.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 6 May 2002, 18:06
    19  * Updated 26 Aug 2002 by Drew
    20  * - Added toSimpleString() method, which returns a simplified and hopefully more
    21  *   readable version of the Rational.  i.e. 2/10 -> 1/5, and 10/2 -> 5
    22  * Modified 29 Oct 2002 (v1.2)
    23  * - Improved toSimpleString() to factor more complex rational numbers into
    24  *   a simpler form
    25  *     i.e.
    26  *       10/15 -> 2/3
    27  * - toSimpleString() now accepts a boolean flag, 'allowDecimals' which will
    28  *   display the rational number in decimal form if it fits within 5 digits
    29  *     i.e.
    30  *       3/4 -> 0.75 when allowDecimal == true
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    3120 */
    3221
    3322package com.drew.lang;
    3423
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26
    3527import java.io.Serializable;
    3628
    3729/**
    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;
     37    private static final long serialVersionUID = 510688928138848770L;
    4938
    50     /**
    51      * Holds the denominator.
    52      */
    53     private final int denominator;
     39    /** Holds the numerator. */
     40    private final long _numerator;
    5441
    55     private int maxSimplificationCalculations = 1000;
     42    /** Holds the denominator. */
     43    private final long _denominator;
    5644
    5745    /**
    5846     * Creates a new instance of Rational.  Rational objects are immutable, so
    5947     * once you've set your numerator and denominator values here, you're stuck
    6048     * with them!
    6149     */
    62     public Rational(int numerator, int denominator)
     50    public Rational(long numerator, long denominator)
    6351    {
    64         this.numerator = numerator;
    65         this.denominator = denominator;
     52        _numerator = numerator;
     53        _denominator = denominator;
    6654    }
    6755
    6856    /**
    6957     * Returns the value of the specified number as a <code>double</code>.
    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
    8068    /**
    8169     * Returns the value of the specified number as a <code>float</code>.
    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
    9280    /**
     
    9482     * This may involve rounding or truncation.  This implementation simply
    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
    10593    /**
     
    10795     * This may involve rounding or truncation.  This implementation simply
    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
    118106    /**
     
    120108     * This may involve rounding or truncation.  This implementation simply
    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
    131119    /**
     
    133121     * This may involve rounding or truncation.  This implementation simply
    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();
     129        return (short) doubleValue();
    142130    }
    143131
    144132
    145     /**
    146      * Returns the denominator.
    147      */
    148     public final int getDenominator()
     133    /** Returns the denominator. */
     134    public final long getDenominator()
    149135    {
    150         return this.denominator;
     136        return this._denominator;
    151137    }
    152138
    153     /**
    154      * Returns the numerator.
    155      */
    156     public final int getNumerator()
     139    /** Returns the numerator. */
     140    public final long getNumerator()
    157141    {
    158         return this.numerator;
     142        return this._numerator;
    159143    }
    160144
    161145    /**
    162      * Returns the reciprocal value of this obejct as a new Rational.
     146     * Returns the reciprocal value of this object as a new Rational.
     147     *
    163148     * @return the reciprocal in a new object
    164149     */
     150    @NotNull
    165151    public Rational getReciprocal()
    166152    {
    167         return new Rational(this.denominator, this.numerator);
     153        return new Rational(this._denominator, this._numerator);
    168154    }
    169155
    170     /**
    171      * Checks if this rational number is an Integer, either positive or negative.
    172      */
     156    /** Checks if this rational number is an Integer, either positive or negative. */
    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.
     166     *
     167     * @return a string representation of the object.
    188168     */
     169    @NotNull
    189170    public String toString()
    190171    {
    191         return numerator + "/" + denominator;
     172        return _numerator + "/" + _denominator;
    192173    }
    193174
    194     /**
    195      * Returns the simplest represenation of this Rational's value possible.
    196      */
     175    /** Returns the simplest representation of this Rational's value possible. */
     176    @NotNull
    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 {
    208188            Rational simplifiedInstance = getSimplifiedInstance();
     
    219199    /**
    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    }
    229211
    230212    /**
    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)
     220    @Override
     221    public boolean equals(@Nullable Object obj)
    238222    {
    239         if (!(obj instanceof Rational)) {
     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();
    244227    }
    245228
     229    @Override
     230    public int hashCode()
     231    {
     232        return (23 * (int)_denominator) + (int)_numerator;
     233    }
     234
    246235    /**
    247236     * <p>
    248237     * Simplifies the Rational number.</p>
     
    251240     * <p>
    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
    257246     * it's simpler to check if both d & n are divisible by all numbers from 2 ->
     
    264253     *    4   Math.min(denominator, numerator) - 1
    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    {
    277268        if (tooComplexForSimplification()) {
    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        }
    289280        return this;
    290281    }
    291 }
    292  Pas de retour chariot à la fin du fichier
     282}
  • src/com/drew/lang/StringUtil.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.lang;
     23
     24import com.drew.lang.annotations.NotNull;
     25
     26import java.util.Iterator;
     27
     28/** @author Drew Noakes http://drewnoakes.com */
     29public class StringUtil
     30{
     31    public static String join(@NotNull Iterable<? extends CharSequence> strings, @NotNull String delimiter)
     32    {
     33        int capacity = 0;
     34        int delimLength = delimiter.length();
     35
     36        Iterator<? extends CharSequence> iter = strings.iterator();
     37        if (iter.hasNext())
     38            capacity += iter.next().length() + delimLength;
     39
     40        StringBuilder buffer = new StringBuilder(capacity);
     41        iter = strings.iterator();
     42        if (iter.hasNext()) {
     43            buffer.append(iter.next());
     44            while (iter.hasNext()) {
     45                buffer.append(delimiter);
     46                buffer.append(iter.next());
     47            }
     48        }
     49        return buffer.toString();
     50    }
     51
     52    public static <T extends CharSequence> String join(@NotNull T[] strings, @NotNull String delimiter)
     53    {
     54        int capacity = 0;
     55        int delimLength = delimiter.length();
     56        for (T value : strings)
     57            capacity += value.length() + delimLength;
     58
     59        StringBuilder buffer = new StringBuilder(capacity);
     60        boolean first = true;
     61        for (T value : strings) {
     62            if (!first) {
     63                buffer.append(delimiter);
     64            } else {
     65                first = false;
     66            }
     67            buffer.append(value);
     68        }
     69        return buffer.toString();
     70    }
     71}
  • src/com/drew/lang/StringUtil.java

  • src/com/drew/metadata/Age.java

    Modification de propriétés sur src/com/drew/lang/StringUtil.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26
     27/**
     28 * Represents an age in years, months, days, hours, minutes and seconds.
     29 * <p/>
     30 * Used by certain Panasonic cameras which have face recognition features.
     31 *
     32 * @author Drew Noakes http://drewnoakes.com
     33 */
     34public class Age
     35{
     36    private int _years;
     37    private int _months;
     38    private int _days;
     39    private int _hours;
     40    private int _minutes;
     41    private int _seconds;
     42
     43    /**
     44     * Parses an age object from the string format used by Panasonic cameras:
     45     * <code>0031:07:15 00:00:00</code>
     46     * @param s The String in format <code>0031:07:15 00:00:00</code>.
     47     * @return The parsed Age object, or null if the value could not be parsed
     48     */
     49    @Nullable
     50    public static Age fromPanasonicString(@NotNull String s)
     51    {
     52        if (s == null)
     53            throw new NullPointerException();
     54
     55        if (s.length() != 19 || s.startsWith("9999:99:99"))
     56            return null;
     57
     58        try {
     59            int years = Integer.parseInt(s.substring(0, 4));
     60            int months = Integer.parseInt(s.substring(5, 7));
     61            int days = Integer.parseInt(s.substring(8, 10));
     62            int hours = Integer.parseInt(s.substring(11, 13));
     63            int minutes = Integer.parseInt(s.substring(14, 16));
     64            int seconds = Integer.parseInt(s.substring(17, 19));
     65
     66            return new Age(years, months, days, hours, minutes, seconds);
     67        }
     68        catch (NumberFormatException ignored)
     69        {
     70            return null;
     71        }
     72    }
     73
     74    public Age(int years, int months, int days, int hours, int minutes, int seconds)
     75    {
     76        _years = years;
     77        _months = months;
     78        _days = days;
     79        _hours = hours;
     80        _minutes = minutes;
     81        _seconds = seconds;
     82    }
     83
     84    public int getYears()
     85    {
     86        return _years;
     87    }
     88
     89    public int getMonths()
     90    {
     91        return _months;
     92    }
     93
     94    public int getDays()
     95    {
     96        return _days;
     97    }
     98
     99    public int getHours()
     100    {
     101        return _hours;
     102    }
     103
     104    public int getMinutes()
     105    {
     106        return _minutes;
     107    }
     108
     109    public int getSeconds()
     110    {
     111        return _seconds;
     112    }
     113
     114    @Override
     115    public String toString()
     116    {
     117        return String.format("%04d:%02d:%02d %02d:%02d:%02d", _years, _months, _days, _hours, _minutes, _seconds);
     118    }
     119
     120    public String toFriendlyString()
     121    {
     122        StringBuilder result = new StringBuilder();
     123        appendAgePart(result, _years, "year");
     124        appendAgePart(result, _months, "month");
     125        appendAgePart(result, _days, "day");
     126        appendAgePart(result, _hours, "hour");
     127        appendAgePart(result, _minutes, "minute");
     128        appendAgePart(result, _seconds, "second");
     129        return result.toString();
     130    }
     131
     132    private static void appendAgePart(StringBuilder result, final int num, final String singularName)
     133    {
     134        if (num == 0)
     135            return;
     136        if (result.length()!=0)
     137            result.append(' ');
     138        result.append(num).append(' ').append(singularName);
     139        if (num != 1)
     140            result.append('s');
     141    }
     142
     143    @Override
     144    public boolean equals(Object o)
     145    {
     146        if (this == o) return true;
     147        if (o == null || getClass() != o.getClass()) return false;
     148
     149        Age age = (Age)o;
     150
     151        if (_days != age._days) return false;
     152        if (_hours != age._hours) return false;
     153        if (_minutes != age._minutes) return false;
     154        if (_months != age._months) return false;
     155        if (_seconds != age._seconds) return false;
     156        if (_years != age._years) return false;
     157
     158        return true;
     159    }
     160
     161    @Override
     162    public int hashCode()
     163    {
     164        int result = _years;
     165        result = 31 * result + _months;
     166        result = 31 * result + _days;
     167        result = 31 * result + _hours;
     168        result = 31 * result + _minutes;
     169        result = 31 * result + _seconds;
     170        return result;
     171    }
     172}
  • src/com/drew/metadata/Age.java

  • src/com/drew/metadata/DefaultTagDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/Age.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
    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    {
    3147        String hex = Integer.toHexString(tagType).toUpperCase();
    3248        while (hex.length() < 4) hex = "0" + hex;
    3349        return "Unknown tag 0x" + hex;
    3450    }
    35 
    36     public String getDescription(int tagType)
    37     {
    38         return _directory.getString(tagType);
    39     }
    4051}
  • src/com/drew/metadata/Directory.java

     
    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;
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26import com.drew.lang.annotations.SuppressWarnings;
    2027
    21 import java.io.Serializable;
     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;
     43    // TODO get Array methods need to return cloned data, to maintain this directory's integrity
    3944
    40     /**
    41      * The descriptor used to interperet tag values.
    42      */
    43     protected TagDescriptor _descriptor;
     45    /** Map of values hashed by type identifiers. */
     46    @NotNull
     47    protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
    4448
    4549    /**
    4650     * A convenient list holding tag values in the order in which they were stored.
    4751     * This is used for creation of an iterator, and for counting the number of
    4852     * defined tags.
    4953     */
    50     protected final List _definedTagList;
     54    @NotNull
     55    protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
    5156
    52     private List _errorList;
     57    @NotNull
     58    private final Collection<String> _errorList = new ArrayList<String>(4);
    5359
     60    /** The descriptor used to interpret tag values. */
     61    protected TagDescriptor _descriptor;
     62
    5463// ABSTRACT METHODS
    5564
    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();
     78    @NotNull
     79    protected abstract HashMap<Integer, String> getTagNameMap();
    6780
    68 // CONSTRUCTORS
     81    protected Directory()
     82    {}
    6983
    70     /**
    71      * Creates a new Directory.
    72      */
    73     public Directory()
    74     {
    75         _tagMap = new HashMap();
    76         _definedTagList = new ArrayList();
    77     }
    78 
    7984// VARIOUS METHODS
    8085
    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()
     103    @NotNull
     104    public Collection<Tag> getTags()
    96105    {
    97         return _definedTagList.iterator();
     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     */
    104114    public int getTagCount()
     
    107117    }
    108118
    109119    /**
    110      * Sets the descriptor used to interperet tag values.
    111      * @param descriptor the descriptor used to interperet tag values
     120     * Sets the descriptor used to interpret tag values.
     121     *
     122     * @param descriptor the descriptor used to interpret tag values
    112123     */
    113     public void setDescriptor(TagDescriptor descriptor)
     124    @java.lang.SuppressWarnings({ "ConstantConditions" })
     125    public void setDescriptor(@NotNull TagDescriptor descriptor)
    114126    {
    115         if (descriptor==null) {
     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)
     132    /**
     133     * Registers an error message with this directory.
     134     *
     135     * @param message an error message.
     136     */
     137    public void addError(@NotNull String message)
    122138    {
    123         if (_errorList==null) {
    124             _errorList = new ArrayList();
    125         }
    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);
     149        return _errorList.size() > 0;
    132150    }
    133151
    134     public Iterator getErrors()
     152    /**
     153     * Used to iterate over any error messages contained in this directory.
     154     *
     155     * @return an iterable collection of error message strings.
     156     */
     157    @NotNull
     158    public Iterable<String> getErrors()
    135159    {
    136         return _errorList.iterator();
     160        return _errorList;
    137161    }
    138162
     163    /** Returns the count of error messages in this directory. */
    139164    public int getErrorCount()
    140165    {
    141166        return _errorList.size();
     
    144169// TAG SETTERS
    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));
     179        setObject(tagType, value);
    154180    }
    155181
    156182    /**
    157      * Sets a double value for the specified tag.
    158      * @param tagType the tag's value as an int
    159      * @param value the value for the specified tag as a double
     183     * Sets an <code>int[]</code> (array) for the specified tag.
     184     *
     185     * @param tagType the tag identifier
     186     * @param ints    the int array to store
    160187     */
    161     public void setDouble(int tagType, double value)
     188    public void setIntArray(int tagType, @NotNull int[] ints)
    162189    {
    163         setObject(tagType, new Double(value));
     190        setObjectArray(tagType, ints);
    164191    }
    165192
    166193    /**
    167      * Sets a float value for the specified tag.
     194     * Sets a <code>float</code> value for the specified tag.
     195     *
    168196     * @param tagType the tag's value as an int
    169      * @param value the value for the specified tag as a float
     197     * @param value   the value for the specified tag as a float
    170198     */
    171199    public void setFloat(int tagType, float value)
    172200    {
    173         setObject(tagType, new Float(value));
     201        setObject(tagType, value);
    174202    }
    175203
    176204    /**
    177      * Sets an int value for the specified tag.
     205     * Sets a <code>float[]</code> (array) for the specified tag.
     206     *
     207     * @param tagType the tag identifier
     208     * @param floats  the float array to store
     209     */
     210    public void setFloatArray(int tagType, @NotNull float[] floats)
     211    {
     212        setObjectArray(tagType, floats);
     213    }
     214
     215    /**
     216     * Sets a <code>double</code> value for the specified tag.
     217     *
    178218     * @param tagType the tag's value as an int
    179      * @param value the value for the specified tag as a String
     219     * @param value   the value for the specified tag as a double
    180220     */
    181     public void setString(int tagType, String value)
     221    public void setDouble(int tagType, double value)
    182222    {
    183223        setObject(tagType, value);
    184224    }
    185225
    186226    /**
    187      * Sets an int value for the specified tag.
    188      * @param tagType the tag's value as an int
    189      * @param value the value for the specified tag as a boolean
     227     * Sets a <code>double[]</code> (array) for the specified tag.
     228     *
     229     * @param tagType the tag identifier
     230     * @param doubles the double array to store
    190231     */
    191     public void setBoolean(int tagType, boolean value)
     232    public void setDoubleArray(int tagType, @NotNull double[] doubles)
    192233    {
    193         setObject(tagType, new Boolean(value));
     234        setObjectArray(tagType, doubles);
    194235    }
    195236
    196237    /**
    197      * Sets a long value for the specified tag.
     238     * Sets a <code>String</code> value for the specified tag.
     239     *
    198240     * @param tagType the tag's value as an int
    199      * @param value the value for the specified tag as a long
     241     * @param value   the value for the specified tag as a String
    200242     */
    201     public void setLong(int tagType, long value)
     243    @java.lang.SuppressWarnings({ "ConstantConditions" })
     244    public void setString(int tagType, @NotNull String value)
    202245    {
    203         setObject(tagType, new Long(value));
     246        if (value == null)
     247            throw new NullPointerException("cannot set a null String");
     248        setObject(tagType, value);
    204249    }
    205250
    206251    /**
    207      * Sets a java.util.Date value for the specified tag.
     252     * Sets a <code>String[]</code> (array) for the specified tag.
     253     *
     254     * @param tagType the tag identifier
     255     * @param strings the String array to store
     256     */
     257    public void setStringArray(int tagType, @NotNull String[] strings)
     258    {
     259        setObjectArray(tagType, strings);
     260    }
     261
     262    /**
     263     * Sets a <code>boolean</code> value for the specified tag.
     264     *
    208265     * @param tagType the tag's value as an int
    209      * @param value the value for the specified tag as a java.util.Date
     266     * @param value   the value for the specified tag as a boolean
    210267     */
    211     public void setDate(int tagType, java.util.Date value)
     268    public void setBoolean(int tagType, boolean value)
    212269    {
    213270        setObject(tagType, value);
    214271    }
    215272
    216273    /**
    217      * Sets a Rational value for the specified tag.
     274     * Sets a <code>long</code> value for the specified tag.
     275     *
    218276     * @param tagType the tag's value as an int
    219      * @param rational rational number
     277     * @param value   the value for the specified tag as a long
    220278     */
    221     public void setRational(int tagType, Rational rational)
     279    public void setLong(int tagType, long value)
    222280    {
    223         setObject(tagType, rational);
     281        setObject(tagType, value);
    224282    }
    225283
    226284    /**
    227      * Sets a Rational array for the specified tag.
    228      * @param tagType the tag identifier
    229      * @param rationals the Rational array to store
     285     * Sets a <code>java.util.Date</code> value for the specified tag.
     286     *
     287     * @param tagType the tag's value as an int
     288     * @param value   the value for the specified tag as a java.util.Date
    230289     */
    231     public void setRationalArray(int tagType, Rational[] rationals)
     290    public void setDate(int tagType, @NotNull java.util.Date value)
    232291    {
    233         setObjectArray(tagType, rationals);
     292        setObject(tagType, value);
    234293    }
    235294
    236295    /**
    237      * Sets an int array for the specified tag.
    238      * @param tagType the tag identifier
    239      * @param ints the int array to store
     296     * Sets a <code>Rational</code> value for the specified tag.
     297     *
     298     * @param tagType  the tag's value as an int
     299     * @param rational rational number
    240300     */
    241     public void setIntArray(int tagType, int[] ints)
     301    public void setRational(int tagType, @NotNull Rational rational)
    242302    {
    243         setObjectArray(tagType, ints);
     303        setObject(tagType, rational);
    244304    }
    245305
    246306    /**
    247      * Sets a byte array for the specified tag.
    248      * @param tagType the tag identifier
    249      * @param bytes the byte array to store
     307     * Sets a <code>Rational[]</code> (array) for the specified tag.
     308     *
     309     * @param tagType   the tag identifier
     310     * @param rationals the Rational array to store
    250311     */
    251     public void setByteArray(int tagType, byte[] bytes)
     312    public void setRationalArray(int tagType, @NotNull Rational[] rationals)
    252313    {
    253         setObjectArray(tagType, bytes);
     314        setObjectArray(tagType, rationals);
    254315    }
    255316
    256317    /**
    257      * Sets a String array for the specified tag.
     318     * Sets a <code>byte[]</code> (array) for the specified tag.
     319     *
    258320     * @param tagType the tag identifier
    259      * @param strings the String array to store
     321     * @param bytes   the byte array to store
    260322     */
    261     public void setStringArray(int tagType, String[] strings)
     323    public void setByteArray(int tagType, @NotNull byte[] bytes)
    262324    {
    263         setObjectArray(tagType, strings);
     325        setObjectArray(tagType, bytes);
    264326    }
    265327
    266328    /**
    267      * Private helper method, containing common functionality for all 'add'
    268      * methods.
     329     * Sets a <code>Object</code> for the specified tag.
     330     *
    269331     * @param tagType the tag's value as an int
    270      * @param value the value for the specified tag
     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)
     335    @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
     336    public void setObject(int tagType, @NotNull Object value)
    274337    {
    275         if (value==null) {
     338        if (value == null)
    276339            throw new NullPointerException("cannot set a null object");
    277         }
    278340
    279         Integer key = new Integer(tagType);
    280         if (!_tagMap.containsKey(key)) {
     341        if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
    281342            _definedTagList.add(new Tag(tagType, this));
    282343        }
    283         _tagMap.put(key, value);
     344//        else {
     345//            final Object oldValue = _tagMap.get(tagType);
     346//            if (!oldValue.equals(value))
     347//                addError(String.format("Overwritten tag 0x%s (%s).  Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value));
     348//        }
     349        _tagMap.put(tagType, value);
    284350    }
    285351
    286352    /**
    287      * Private helper method, containing common functionality for all 'add...Array'
    288      * methods.
     353     * Sets an array <code>Object</code> for the specified tag.
     354     *
    289355     * @param tagType the tag's value as an int
    290      * @param array the array of values for the specified tag
     356     * @param array   the array of values for the specified tag
    291357     */
    292     public void setObjectArray(int tagType, Object array)
     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
    295361        setObject(tagType, array);
     
    298364// TAG GETTERS
    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    {
     383        Integer integer = getInteger(tagType);
     384        if (integer!=null)
     385            return integer;
     386
    305387        Object o = getObject(tagType);
    306         if (o==null) {
    307             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    308         } else if (o instanceof String) {
     388        if (o == null)
     389            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     390        throw new MetadataException("Tag '" + tagType + "' cannot be converted to int.  It is of type '" + o.getClass() + "'.");
     391    }
     392
     393    /**
     394     * Returns the specified tag's value as an Integer, if possible.  Every attempt to represent the tag's value as an
     395     * Integer is taken.  Here is a list of the action taken depending upon the tag's original type:
     396     * <ul>
     397     * <li> int - Return unchanged
     398     * <li> Number - Return an int value (real numbers are truncated)
     399     * <li> Rational - Truncate any fractional part and returns remaining int
     400     * <li> String - Attempt to parse string as an int.  If this fails, convert the char[] to an int (using shifts and OR)
     401     * <li> Rational[] - Return int value of first item in array if length &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);
    311419            } catch (NumberFormatException nfe) {
     
    313421                String s = (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;
    321429            }
     
    323431            return ((Number)o).intValue();
    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() + "'.");
     445        return null;
    338446    }
    339447
    340     // TODO get Array methods need to return cloned data, to maintain this directory's integrity
    341 
    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[]
     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.
    349454     */
    350     public String[] getStringArray(int tagType) throws MetadataException
     455    @Nullable
     456    public String[] getStringArray(int tagType)
    351457    {
    352458        Object o = getObject(tagType);
    353         if (o==null) {
    354             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    355         } else if (o instanceof String[]) {
     459        if (o == null)
     460            return null;
     461        if (o instanceof String[])
    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
    392493     */
    393     public int[] getIntArray(int tagType) throws MetadataException
     494    @Nullable
     495    public int[] getIntArray(int tagType)
    394496    {
    395497        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[]) {
     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
    433539     */
    434     public byte[] getByteArray(int tagType) throws MetadataException
     540    @Nullable
     541    public byte[] getByteArray(int tagType)
    435542    {
    436543        Object o = getObject(tagType);
    437         if (o==null) {
    438             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
     544        if (o == null) {
     545            return null;
    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            }
    445552            return bytes;
     
    448555        } else if (o instanceof int[]) {
    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() + "'.");
     570        if (o instanceof Integer)
     571            return new byte[] { ((Integer)o).byteValue() };
     572
     573        return null;
    464574    }
    465575
    466     /**
    467      * Returns the specified tag's value as a double, if possible.
    468      */
     576    /** Returns the specified tag's value as a double, if possible. */
    469577    public double getDouble(int tagType) throws MetadataException
    470578    {
     579        Double value = getDoubleObject(tagType);
     580        if (value!=null)
     581            return value;
    471582        Object o = getObject(tagType);
    472         if (o==null) {
    473             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    474         } else if (o instanceof String) {
     583        if (o == null)
     584            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     585        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double.  It is of type '" + o.getClass() + "'.");
     586    }
     587    /** Returns the specified tag's value as a Double.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     588    @Nullable
     589    public Double getDoubleObject(int tagType)
     590    {
     591        Object o = getObject(tagType);
     592        if (o == null)
     593            return null;
     594        if (o instanceof String) {
    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);
     598                return null;
    479599            }
    480         } else if (o instanceof Number) {
     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() + "'.");
     603
     604        return null;
    484605    }
    485606
    486     /**
    487      * Returns the specified tag's value as a float, if possible.
    488      */
     607    /** Returns the specified tag's value as a float, if possible. */
    489608    public float getFloat(int tagType) throws MetadataException
    490609    {
     610        Float value = getFloatObject(tagType);
     611        if (value!=null)
     612            return value;
    491613        Object o = getObject(tagType);
    492         if (o==null) {
    493             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    494         } else if (o instanceof String) {
     614        if (o == null)
     615            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     616        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float.  It is of type '" + o.getClass() + "'.");
     617    }
     618
     619    /** Returns the specified tag's value as a float.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     620    @Nullable
     621    public Float getFloatObject(int tagType)
     622    {
     623        Object o = getObject(tagType);
     624        if (o == null)
     625            return null;
     626        if (o instanceof String) {
    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);
     630                return null;
    499631            }
    500         } else if (o instanceof Number) {
     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() + "'.");
     635        return null;
    504636    }
    505637
    506     /**
    507      * Returns the specified tag's value as a long, if possible.
    508      */
     638    /** Returns the specified tag's value as a long, if possible. */
    509639    public long getLong(int tagType) throws MetadataException
    510640    {
     641        Long value = getLongObject(tagType);
     642        if (value!=null)
     643            return value;
    511644        Object o = getObject(tagType);
    512         if (o==null) {
    513             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    514         } else if (o instanceof String) {
     645        if (o == null)
     646            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     647        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long.  It is of type '" + o.getClass() + "'.");
     648    }
     649
     650    /** Returns the specified tag's value as a long.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     651    @Nullable
     652    public Long getLongObject(int tagType)
     653    {
     654        Object o = getObject(tagType);
     655        if (o == null)
     656            return null;
     657        if (o instanceof String) {
    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);
     661                return null;
    519662            }
    520         } else if (o instanceof Number) {
     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() + "'.");
     666        return null;
    524667    }
    525668
    526     /**
    527      * Returns the specified tag's value as a boolean, if possible.
    528      */
     669    /** Returns the specified tag's value as a boolean, if possible. */
    529670    public boolean getBoolean(int tagType) throws MetadataException
    530671    {
     672        Boolean value = getBooleanObject(tagType);
     673        if (value!=null)
     674            return value;
    531675        Object o = getObject(tagType);
    532         if (o==null) {
    533             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    534         } else if (o instanceof Boolean) {
    535             return ((Boolean)o).booleanValue();
    536         } else if (o instanceof String) {
     676        if (o == null)
     677            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
     678        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean.  It is of type '" + o.getClass() + "'.");
     679    }
     680
     681    /** Returns the specified tag's value as a boolean.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
     682    @Nullable
     683    @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
     684    public Boolean getBooleanObject(int tagType)
     685    {
     686        Object o = getObject(tagType);
     687        if (o == null)
     688            return null;
     689        if (o instanceof Boolean)
     690            return (Boolean)o;
     691        if (o instanceof String) {
    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);
     695                return null;
    541696            }
    542         } else if (o instanceof Number) {
    543             return (((Number)o).doubleValue()!=0);
    544697        }
    545         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean.  It is of type '" + o.getClass() + "'.");
     698        if (o instanceof Number)
     699            return (((Number)o).doubleValue() != 0);
     700        return null;
    546701    }
    547702
    548703    /**
    549      * Returns the specified tag's value as a java.util.Date, if possible.
     704     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
     705     * <p/>
     706     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
     707     * the current {@link TimeZone}.  If the {@link TimeZone} is known, call the overload that accepts one as an argument.
    550708     */
    551     public java.util.Date getDate(int tagType) throws MetadataException
     709    @Nullable
     710    public java.util.Date getDate(int tagType)
    552711    {
     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    {
    553725        Object o = getObject(tagType);
    554         if (o==null) {
    555             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    556         } else if (o instanceof java.util.Date) {
     726
     727        if (o == null)
     728            return null;
     729
     730        if (o instanceof java.util.Date)
    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() + "'.");
     755        return null;
    578756    }
    579757
    580     /**
    581      * Returns the specified tag's value as a Rational, if possible.
    582      */
    583     public Rational getRational(int tagType) throws MetadataException
     758    /** Returns the specified tag's value as a Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
     759    @Nullable
     760    public Rational getRational(int tagType)
    584761    {
    585762        Object o = getObject(tagType);
    586         if (o==null) {
    587             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    588         } else if (o instanceof Rational) {
     763
     764        if (o == null)
     765            return null;
     766
     767        if (o instanceof Rational)
    589768            return (Rational)o;
    590         }
    591         throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational.  It is of type '" + o.getClass() + "'.");
     769        if (o instanceof Integer)
     770            return new Rational((Integer)o, 1);
     771        if (o instanceof Long)
     772            return new Rational((Long)o, 1);
     773
     774        // NOTE not doing conversions for real number types
     775
     776        return null;
    592777    }
    593778
    594     public Rational[] getRationalArray(int tagType) throws MetadataException
     779    /** Returns the specified tag's value as an array of Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
     780    @Nullable
     781    public Rational[] getRationalArray(int tagType)
    595782    {
    596783        Object o = getObject(tagType);
    597         if (o==null) {
    598             throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");
    599         } else if (o instanceof Rational[]) {
     784        if (o == null)
     785            return null;
     786
     787        if (o instanceof Rational[])
    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
    605793    /**
    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
    617807        if (o instanceof Rational)
    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));
     837                    addError("Unexpected array component type: " + componentType.getName());
    635838            }
    636             return sbuffer.toString();
     839            return string.toString();
    637840        }
    638841
     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
     866     * @return the tag's value as an Object if available, else <code>null</code>
    646867     */
     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
    652875// OTHER METHODS
    653876
    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
    673897    /**
    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
    680903     */
    681     public String getDescription(int tagType) throws MetadataException
     904    @Nullable
     905    public String getDescription(int tagType)
    682906    {
    683         if (_descriptor==null) {
    684             throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided");
    685         }
    686 
     907        assert(_descriptor != null);
    687908        return _descriptor.getDescription(tagType);
    688909    }
    689910}
  • src/com/drew/metadata/exif/CanonMakernoteDescriptor.java

     
    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();
     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();
    114127            default:
    115                 return _directory.getString(tagType);
     128                return super.getDescription(tagType);
    116129        }
    117130    }
    118131
    119     public String getLongExposureNoiseReductionDescription() throws MetadataException
     132    @Nullable
     133    public String getSerialNumberDescription()
    120134    {
    121         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION)) return null;
    122         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
     135        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_SERIAL_NUMBER);
     136        if (value==null)
     137            return null;
     138        return String.format("%04X%05d", (value >> 8) & 0xFF, value & 0xFF);
     139    }
     140
     141/*
     142    @Nullable
     143    public String getLongExposureNoiseReductionDescription()
     144    {
     145        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
     146        if (value==null)
     147            return null;
    123148        switch (value) {
    124149            case 0:     return "Off";
    125150            case 1:     return "On";
    126151            default:    return "Unknown (" + value + ")";
    127152        }
    128153    }
    129     public String getShutterAutoExposureLockButtonDescription() throws MetadataException
     154
     155    @Nullable
     156    public String getShutterAutoExposureLockButtonDescription()
    130157    {
    131         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS)) return null;
    132         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
     158        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
     159        if (value==null)
     160            return null;
    133161        switch (value) {
    134162            case 0:     return "AF/AE lock";
    135163            case 1:     return "AE lock/AF";
     
    138166            default:    return "Unknown (" + value + ")";
    139167        }
    140168    }
    141     public String getMirrorLockupDescription() throws MetadataException
     169
     170    @Nullable
     171    public String getMirrorLockupDescription()
    142172    {
    143         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP)) return null;
    144         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
     173        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
     174        if (value==null)
     175            return null;
    145176        switch (value) {
    146177            case 0:     return "Disabled";
    147178            case 1:     return "Enabled";
    148179            default:    return "Unknown (" + value + ")";
    149180        }
    150181    }
    151     public String getTvAndAvExposureLevelDescription() throws MetadataException
     182
     183    @Nullable
     184    public String getTvAndAvExposureLevelDescription()
    152185    {
    153         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL)) return null;
    154         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
     186        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
     187        if (value==null)
     188            return null;
    155189        switch (value) {
    156190            case 0:     return "1/2 stop";
    157191            case 1:     return "1/3 stop";
    158192            default:    return "Unknown (" + value + ")";
    159193        }
    160194    }
    161     public String getAutoFocusAssistLightDescription() throws MetadataException
     195
     196    @Nullable
     197    public String getAutoFocusAssistLightDescription()
    162198    {
    163         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT)) return null;
    164         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
     199        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
     200        if (value==null)
     201            return null;
    165202        switch (value) {
    166203            case 0:     return "On (Auto)";
    167204            case 1:     return "Off";
    168205            default:    return "Unknown (" + value + ")";
    169206        }
    170207    }
    171     public String getShutterSpeedInAvModeDescription() throws MetadataException
     208
     209    @Nullable
     210    public String getShutterSpeedInAvModeDescription()
    172211    {
    173         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE)) return null;
    174         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
     212        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
     213        if (value==null)
     214            return null;
    175215        switch (value) {
    176216            case 0:     return "Automatic";
    177217            case 1:     return "1/200 (fixed)";
    178218            default:    return "Unknown (" + value + ")";
    179219        }
    180220    }
    181     public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() throws MetadataException
     221
     222    @Nullable
     223    public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription()
    182224    {
    183         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING)) return null;
    184         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING);
     225        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING);
     226        if (value==null)
     227            return null;
    185228        switch (value) {
    186229            case 0:     return "0,-,+ / Enabled";
    187230            case 1:     return "0,-,+ / Disabled";
     
    190233            default:    return "Unknown (" + value + ")";
    191234        }
    192235    }
    193     public String getShutterCurtainSyncDescription() throws MetadataException
     236
     237    @Nullable
     238    public String getShutterCurtainSyncDescription()
    194239    {
    195         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC)) return null;
    196         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
     240        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
     241        if (value==null)
     242            return null;
    197243        switch (value) {
    198244            case 0:     return "1st Curtain Sync";
    199245            case 1:     return "2nd Curtain Sync";
    200246            default:    return "Unknown (" + value + ")";
    201247        }
    202248    }
    203     public String getLensAutoFocusStopButtonDescription() throws MetadataException
     249
     250    @Nullable
     251    public String getLensAutoFocusStopButtonDescription()
    204252    {
    205         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP)) return null;
    206         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
     253        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
     254        if (value==null)
     255            return null;
    207256        switch (value) {
    208257            case 0:     return "AF stop";
    209258            case 1:     return "Operate AF";
     
    211260            default:    return "Unknown (" + value + ")";
    212261        }
    213262    }
    214     public String getFillFlashReductionDescription() throws MetadataException
     263
     264    @Nullable
     265    public String getFillFlashReductionDescription()
    215266    {
    216         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION)) return null;
    217         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
     267        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
     268        if (value==null)
     269            return null;
    218270        switch (value) {
    219271            case 0:     return "Enabled";
    220272            case 1:     return "Disabled";
    221273            default:    return "Unknown (" + value + ")";
    222274        }
    223275    }
    224     public String getMenuButtonReturnPositionDescription() throws MetadataException
     276
     277    @Nullable
     278    public String getMenuButtonReturnPositionDescription()
    225279    {
    226         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN)) return null;
    227         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
     280        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
     281        if (value==null)
     282            return null;
    228283        switch (value) {
    229284            case 0:     return "Top";
    230285            case 1:     return "Previous (volatile)";
     
    232287            default:    return "Unknown (" + value + ")";
    233288        }
    234289    }
    235     public String getSetButtonFunctionWhenShootingDescription() throws MetadataException
     290
     291    @Nullable
     292    public String getSetButtonFunctionWhenShootingDescription()
    236293    {
    237         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION)) return null;
    238         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
     294        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
     295        if (value==null)
     296            return null;
    239297        switch (value) {
    240298            case 0:     return "Not Assigned";
    241299            case 1:     return "Change Quality";
     
    244302            default:    return "Unknown (" + value + ")";
    245303        }
    246304    }
    247     public String getSensorCleaningDescription() throws MetadataException
     305
     306    @Nullable
     307    public String getSensorCleaningDescription()
    248308    {
    249         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING)) return null;
    250         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
     309        Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
     310        if (value==null)
     311            return null;
    251312        switch (value) {
    252313            case 0:     return "Disabled";
    253314            case 1:     return "Enabled";
    254315            default:    return "Unknown (" + value + ")";
    255316        }
    256317    }
     318*/
    257319
    258     public String getFlashBiasDescription() throws MetadataException
     320    @Nullable
     321    public String getFlashBiasDescription()
    259322    {
    260         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS)) return null;
     323        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS);
    261324
    262         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS);
     325        if (value==null)
     326            return null;
    263327
    264328        boolean isNegative = false;
    265329        if (value > 0xF000)
     
    277341        return ((isNegative) ? "-" : "") + Float.toString(value / 32f) + " EV";
    278342    }
    279343
    280     public String getAfPointUsedDescription() throws MetadataException
     344    @Nullable
     345    public String getAfPointUsedDescription()
    281346    {
    282         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED)) return null;
    283         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED);
     347        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED);
     348        if (value==null)
     349            return null;
    284350        if ((value & 0x7) == 0) {
    285351            return "Right";
    286352        } else if ((value & 0x7) == 1) {
     
    292358        }
    293359    }
    294360
    295     public String getWhiteBalanceDescription() throws MetadataException
     361    @Nullable
     362    public String getWhiteBalanceDescription()
    296363    {
    297         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE)) return null;
    298         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE);
     364        Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE);
     365        if (value==null)
     366            return null;
    299367        switch (value) {
    300368            case 0:
    301369                return "Auto";
     
    306374            case 3:
    307375                return "Tungsten";
    308376            case 4:
    309                 return "Flourescent";
     377                return "Florescent";
    310378            case 5:
    311379                return "Flash";
    312380            case 6:
     
    316384        }
    317385    }
    318386
    319     public String getFocusMode2Description() throws MetadataException
     387    @Nullable
     388    public String getFocusMode2Description()
    320389    {
    321         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2)) return null;
    322         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2);
     390        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2);
     391        if (value==null)
     392            return null;
    323393        switch (value) {
    324394            case 0:
    325395                return "Single";
     
    330400        }
    331401    }
    332402
    333     public String getFlashDetailsDescription() throws MetadataException
     403    @Nullable
     404    public String getFlashDetailsDescription()
    334405    {
    335         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS)) return null;
    336         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS);
    337         if (((value << 14) & 1) > 0) {
     406        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS);
     407        if (value==null)
     408            return null;
     409        if (((value >> 14) & 1) > 0) {
    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        }
    349421        return "Unknown (" + value + ")";
    350422    }
    351423
    352     public String getFocalUnitsPerMillimetreDescription() throws MetadataException
     424    @Nullable
     425    public String getFocalUnitsPerMillimetreDescription()
    353426    {
    354         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM)) return "";
    355         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM);
     427        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM);
     428        if (value==null)
     429            return null;
    356430        if (value != 0) {
    357431            return Integer.toString(value);
    358432        } else {
     
    360434        }
    361435    }
    362436
    363     public String getShortFocalLengthDescription() throws MetadataException
     437    @Nullable
     438    public String getShortFocalLengthDescription()
    364439    {
    365         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH)) return null;
    366         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH);
     440        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH);
     441        if (value==null)
     442            return null;
    367443        String units = getFocalUnitsPerMillimetreDescription();
    368444        return Integer.toString(value) + " " + units;
    369445    }
    370446
    371     public String getLongFocalLengthDescription() throws MetadataException
     447    @Nullable
     448    public String getLongFocalLengthDescription()
    372449    {
    373         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH)) return null;
    374         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH);
     450        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH);
     451        if (value==null)
     452            return null;
    375453        String units = getFocalUnitsPerMillimetreDescription();
    376454        return Integer.toString(value) + " " + units;
    377455    }
    378456
    379     public String getExposureModeDescription() throws MetadataException
     457    @Nullable
     458    public String getExposureModeDescription()
    380459    {
    381         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE)) return null;
    382         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE);
     460        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE);
     461        if (value==null)
     462            return null;
    383463        switch (value) {
    384464            case 0:
    385465                return "Easy shooting";
     
    398478        }
    399479    }
    400480
    401     public String getAfPointSelectedDescription() throws MetadataException
     481    @Nullable
     482    public String getAfPointSelectedDescription()
    402483    {
    403         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED)) return null;
    404         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED);
     484        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED);
     485        if (value==null)
     486            return null;
    405487        switch (value) {
    406488            case 0x3000:
    407489                return "None (MF)";
     
    418500        }
    419501    }
    420502
    421     public String getMeteringModeDescription() throws MetadataException
     503    @Nullable
     504    public String getMeteringModeDescription()
    422505    {
    423         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE)) return null;
    424         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE);
     506        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE);
     507        if (value==null)
     508            return null;
    425509        switch (value) {
    426510            case 3:
    427511                return "Evaluative";
     
    434518        }
    435519    }
    436520
    437     public String getIsoDescription() throws MetadataException
     521    @Nullable
     522    public String getIsoDescription()
    438523    {
    439         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO)) return null;
    440         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO);
     524        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_ISO);
     525        if (value==null)
     526            return null;
     527
     528        // Canon PowerShot S3 is special
     529        int canonMask = 0x4000;
     530        if ((value & canonMask) > 0)
     531            return "" + (value & ~canonMask);
     532
    441533        switch (value) {
    442534            case 0:
    443535                return "Not specified (see ISOSpeedRatings tag)";
     
    456548        }
    457549    }
    458550
    459     public String getSharpnessDescription() throws MetadataException
     551    @Nullable
     552    public String getSharpnessDescription()
    460553    {
    461         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS)) return null;
    462         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS);
     554        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS);
     555        if (value==null)
     556            return null;
    463557        switch (value) {
    464558            case 0xFFFF:
    465559                return "Low";
     
    472566        }
    473567    }
    474568
    475     public String getSaturationDescription() throws MetadataException
     569    @Nullable
     570    public String getSaturationDescription()
    476571    {
    477         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION)) return null;
    478         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION);
     572        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SATURATION);
     573        if (value==null)
     574            return null;
    479575        switch (value) {
    480576            case 0xFFFF:
    481577                return "Low";
     
    488584        }
    489585    }
    490586
    491     public String getContrastDescription() throws MetadataException
     587    @Nullable
     588    public String getContrastDescription()
    492589    {
    493         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST)) return null;
    494         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST);
     590        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST);
     591        if (value==null)
     592            return null;
    495593        switch (value) {
    496594            case 0xFFFF:
    497595                return "Low";
     
    504602        }
    505603    }
    506604
    507     public String getEasyShootingModeDescription() throws MetadataException
     605    @Nullable
     606    public String getEasyShootingModeDescription()
    508607    {
    509         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE)) return null;
    510         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE);
     608        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE);
     609        if (value==null)
     610            return null;
    511611        switch (value) {
    512612            case 0:
    513613                return "Full auto";
     
    538638        }
    539639    }
    540640
    541     public String getImageSizeDescription() throws MetadataException
     641    @Nullable
     642    public String getImageSizeDescription()
    542643    {
    543         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE)) return null;
    544         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE);
     644        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE);
     645        if (value==null)
     646            return null;
    545647        switch (value) {
    546648            case 0:
    547649                return "Large";
     
    554656        }
    555657    }
    556658
    557     public String getFocusMode1Description() throws MetadataException
     659    @Nullable
     660    public String getFocusMode1Description()
    558661    {
    559         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1)) return null;
    560         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1);
     662        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1);
     663        if (value==null)
     664            return null;
    561665        switch (value) {
    562666            case 0:
    563667                return "One-shot";
     
    579683        }
    580684    }
    581685
    582     public String getContinuousDriveModeDescription() throws MetadataException
     686    @Nullable
     687    public String getContinuousDriveModeDescription()
    583688    {
    584         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE)) return null;
    585         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE);
     689        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE);
     690        if (value==null)
     691            return null;
    586692        switch (value) {
    587693            case 0:
    588                 if (_directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY) == 0) {
    589                     return "Single shot";
    590                 } else {
    591                     return "Single shot with self-timer";
    592                 }
     694                final Integer delay = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY);
     695                if (delay!=null)
     696                    return delay == 0 ? "Single shot" : "Single shot with self-timer";
    593697            case 1:
    594698                return "Continuous";
    595             default:
    596                 return "Unknown (" + value + ")";
    597699        }
     700        return "Unknown (" + value + ")";
    598701    }
    599702
    600     public String getFlashModeDescription() throws MetadataException
     703    @Nullable
     704    public String getFlashModeDescription()
    601705    {
    602         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE)) return null;
    603         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE);
     706        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE);
     707        if (value==null)
     708            return null;
    604709        switch (value) {
    605710            case 0:
    606711                return "No flash fired";
     
    618723                return "On and red-eye reduction";
    619724            case 16:
    620725                // note: this value not set on Canon D30
    621                 return "Extenal flash";
     726                return "External flash";
    622727            default:
    623728                return "Unknown (" + value + ")";
    624729        }
    625730    }
    626731
    627     public String getSelfTimerDelayDescription() throws MetadataException
     732    @Nullable
     733    public String getSelfTimerDelayDescription()
    628734    {
    629         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY)) return null;
    630         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY);
     735        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY);
     736        if (value==null)
     737            return null;
    631738        if (value == 0) {
    632739            return "Self timer not used";
    633740        } else {
     
    636743        }
    637744    }
    638745
    639     public String getMacroModeDescription() throws MetadataException
     746    @Nullable
     747    public String getMacroModeDescription()
    640748    {
    641         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE)) return null;
    642         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE);
     749        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE);
     750        if (value==null)
     751            return null;
    643752        switch (value) {
    644753            case 1:
    645754                return "Macro";
     
    650759        }
    651760    }
    652761
    653     public String getQualityDescription() throws MetadataException
     762    @Nullable
     763    public String getQualityDescription()
    654764    {
    655         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY)) return null;
    656         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY);
     765        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_QUALITY);
     766        if (value==null)
     767            return null;
    657768        switch (value) {
    658769            case 2:
    659770                return "Normal";
     
    666777        }
    667778    }
    668779
    669     public String getDigitalZoomDescription() throws MetadataException
     780    @Nullable
     781    public String getDigitalZoomDescription()
    670782    {
    671         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM)) return null;
    672         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM);
     783        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM);
     784        if (value==null)
     785            return null;
    673786        switch (value) {
    674787            case 0:
    675788                return "No digital zoom";
     
    682795        }
    683796    }
    684797
    685     public String getFocusTypeDescription() throws MetadataException
     798    @Nullable
     799    public String getFocusTypeDescription()
    686800    {
    687         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE)) return null;
    688         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE);
     801        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE);
     802        if (value==null)
     803            return null;
    689804        switch (value) {
    690805            case 0:
    691806                return "Manual";
     
    700815        }
    701816    }
    702817
    703     public String getFlashActivityDescription() throws MetadataException
     818    @Nullable
     819    public String getFlashActivityDescription()
    704820    {
    705         if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY)) return null;
    706         int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY);
     821        Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_ACTIVITY);
     822        if (value==null)
     823            return null;
    707824        switch (value) {
    708825            case 0:
    709826                return "Flash did not fire";
  • src/com/drew/metadata/exif/CanonMakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
     
    2631 * Thanks to Bill Richards for his contribution to this makernote directory.
    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;
     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.
    3542
    36     public static final int TAG_CANON_IMAGE_TYPE = 0x0006;
    37     public static final int TAG_CANON_FIRMWARE_VERSION = 0x0007;
    38     public static final int TAG_CANON_IMAGE_NUMBER = 0x0008;
    39     public static final int TAG_CANON_OWNER_NAME = 0x0009;
    40     /**
    41      * To display serial number as on camera use: printf( "%04X%05d", highbyte, lowbyte )
    42      * TODO handle this in CanonMakernoteDescriptor
    43      */
    44     public static final int TAG_CANON_SERIAL_NUMBER = 0x000C;
    45     public static final int TAG_CANON_UNKNOWN_1 = 0x000D;
    46     public static final int TAG_CANON_CUSTOM_FUNCTIONS = 0x000F;
     43    private static final int TAG_CAMERA_SETTINGS_ARRAY  = 0x0001;
     44    private static final int TAG_FOCAL_LENGTH_ARRAY = 0x0002;
     45    private static final int TAG_SHOT_INFO_ARRAY = 0x0004;
     46    private static final int TAG_PANORAMA_ARRAY = 0x0005;
    4747
    48     // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    49     /**
    50      * 1 = Macro
    51      * 2 = Normal
    52      */
    53     public static final int TAG_CANON_STATE1_MACRO_MODE = 0xC101;
    54     public static final int TAG_CANON_STATE1_SELF_TIMER_DELAY = 0xC102;
    55     /**
    56      * 2 = Normal
    57      * 3 = Fine
    58      * 5 = Superfine
    59      */
    60     public static final int TAG_CANON_STATE1_QUALITY = 0xC103;
    61     /**
    62      * 0 = Flash Not Fired
    63      * 1 = Auto
    64      * 2 = On
    65      * 3 = Red Eye Reduction
    66      * 4 = Slow Synchro
    67      * 5 = Auto + Red Eye Reduction
    68      * 6 = On + Red Eye Reduction
    69      * 16 = External Flash
    70      */
    71     public static final int TAG_CANON_STATE1_FLASH_MODE = 0xC104;
    72     /**
    73      * 0 = Single Frame or Timer Mode
    74      * 1 = Continuous
    75      */
    76     public static final int TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE = 0xC105;
    77     public static final int TAG_CANON_STATE1_UNKNOWN_2 = 0xC106;
    78     /**
    79      * 0 = One-Shot
    80      * 1 = AI Servo
    81      * 2 = AI Focus
    82      * 3 = Manual Focus
    83      * 4 = Single
    84      * 5 = Continuous
    85      * 6 = Manual Focus
    86      */
    87     public static final int TAG_CANON_STATE1_FOCUS_MODE_1 = 0xC107;
    88     public static final int TAG_CANON_STATE1_UNKNOWN_3 = 0xC108;
    89     public static final int TAG_CANON_STATE1_UNKNOWN_4 = 0xC109;
    90     /**
    91      * 0 = Large
    92      * 1 = Medium
    93      * 2 = Small
    94      */
    95     public static final int TAG_CANON_STATE1_IMAGE_SIZE = 0xC10A;
    96     /**
    97      * 0 = Full Auto
    98      * 1 = Manual
    99      * 2 = Landscape
    100      * 3 = Fast Shutter
    101      * 4 = Slow Shutter
    102      * 5 = Night
    103      * 6 = Black & White
    104      * 7 = Sepia
    105      * 8 = Portrait
    106      * 9 = Sports
    107      * 10 = Macro / Close-Up
    108      * 11 = Pan Focus
    109      */
    110     public static final int TAG_CANON_STATE1_EASY_SHOOTING_MODE = 0xC10B;
    111     /**
    112      * 0 = No Digital Zoom
    113      * 1 = 2x
    114      * 2 = 4x
    115      */
    116     public static final int TAG_CANON_STATE1_DIGITAL_ZOOM = 0xC10C;
    117     /**
    118      * 0 = Normal
    119      * 1 = High
    120      * 65535 = Low
    121      */
    122     public static final int TAG_CANON_STATE1_CONTRAST = 0xC10D;
    123     /**
    124      * 0 = Normal
    125      * 1 = High
    126      * 65535 = Low
    127      */
    128     public static final int TAG_CANON_STATE1_SATURATION = 0xC10E;
    129     /**
    130      * 0 = Normal
    131      * 1 = High
    132      * 65535 = Low
    133      */
    134     public static final int TAG_CANON_STATE1_SHARPNESS = 0xC10F;
    135     /**
    136      * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
    137      * 15 = Auto ISO
    138      * 16 = ISO 50
    139      * 17 = ISO 100
    140      * 18 = ISO 200
    141      * 19 = ISO 400
    142      */
    143     public static final int TAG_CANON_STATE1_ISO = 0xC110;
    144     /**
    145      * 3 = Evaluative
    146      * 4 = Partial
    147      * 5 = Centre Weighted
    148      */
    149     public static final int TAG_CANON_STATE1_METERING_MODE = 0xC111;
    150     /**
    151      * 0 = Manual
    152      * 1 = Auto
    153      * 3 = Close-up (Macro)
    154      * 8 = Locked (Pan Mode)
    155      */
    156     public static final int TAG_CANON_STATE1_FOCUS_TYPE = 0xC112;
    157     /**
    158      * 12288 = None (Manual Focus)
    159      * 12289 = Auto Selected
    160      * 12290 = Right
    161      * 12291 = Centre
    162      * 12292 = Left
    163      */
    164     public static final int TAG_CANON_STATE1_AF_POINT_SELECTED = 0xC113;
    165     /**
    166      * 0 = Easy Shooting (See Easy Shooting Mode)
    167      * 1 = Program
    168      * 2 = Tv-Priority
    169      * 3 = Av-Priority
    170      * 4 = Manual
    171      * 5 = A-DEP
    172      */
    173     public static final int TAG_CANON_STATE1_EXPOSURE_MODE = 0xC114;
    174     public static final int TAG_CANON_STATE1_UNKNOWN_7 = 0xC115;
    175     public static final int TAG_CANON_STATE1_UNKNOWN_8 = 0xC116;
    176     public static final int TAG_CANON_STATE1_LONG_FOCAL_LENGTH = 0xC117;
    177     public static final int TAG_CANON_STATE1_SHORT_FOCAL_LENGTH = 0xC118;
    178     public static final int TAG_CANON_STATE1_FOCAL_UNITS_PER_MM = 0xC119;
    179     public static final int TAG_CANON_STATE1_UNKNOWN_9 = 0xC11A;
    180     public static final int TAG_CANON_STATE1_UNKNOWN_10 = 0xC11B;
    181     /**
    182      * 0 = Flash Did Not Fire
    183      * 1 = Flash Fired
    184      */
    185     public static final int TAG_CANON_STATE1_FLASH_ACTIVITY = 0xC11C;
    186     public static final int TAG_CANON_STATE1_FLASH_DETAILS = 0xC11D;
    187     public static final int TAG_CANON_STATE1_UNKNOWN_12 = 0xC11E;
    188     public static final int TAG_CANON_STATE1_UNKNOWN_13 = 0xC11F;
    189     /**
    190      * 0 = Focus Mode: Single
    191      * 1 = Focus Mode: Continuous
    192      */
    193     public static final int TAG_CANON_STATE1_FOCUS_MODE_2 = 0xC120;
     48    public static final int TAG_CANON_IMAGE_TYPE            = 0x0006;
     49    public static final int TAG_CANON_FIRMWARE_VERSION      = 0x0007;
     50    public static final int TAG_CANON_IMAGE_NUMBER          = 0x0008;
     51    public static final int TAG_CANON_OWNER_NAME            = 0x0009;
     52    public static final int TAG_CANON_SERIAL_NUMBER         = 0x000C;
     53    public static final int TAG_CAMERA_INFO_ARRAY           = 0x000D; // depends upon model, so leave for now
     54    public static final int TAG_CANON_FILE_LENGTH           = 0x000E;
     55    public static final int TAG_CANON_CUSTOM_FUNCTIONS_ARRAY = 0x000F; // depends upon model, so leave for now
     56    public static final int TAG_MODEL_ID                    = 0x0010;
     57    public static final int TAG_MOVIE_INFO_ARRAY            = 0x0011; // not currently decoded as not sure we see it in still images
     58    private static final int TAG_AF_INFO_ARRAY              = 0x0012; // not currently decoded
     59    public static final int TAG_THUMBNAIL_IMAGE_VALID_AREA  = 0x0013;
     60    public static final int TAG_SERIAL_NUMBER_FORMAT        = 0x0015;
     61    public static final int TAG_SUPER_MACRO                 = 0x001A;
     62    public static final int TAG_DATE_STAMP_MODE             = 0x001C;
     63    public static final int TAG_MY_COLORS                   = 0x001D;
     64    public static final int TAG_FIRMWARE_REVISION           = 0x001E;
     65    public static final int TAG_CATEGORIES                  = 0x0023;
     66    public static final int TAG_FACE_DETECT_ARRAY_1         = 0x0024;
     67    public static final int TAG_FACE_DETECT_ARRAY_2         = 0x0025;
     68    public static final int TAG_AF_INFO_ARRAY_2             = 0x0026;
     69    public static final int TAG_IMAGE_UNIQUE_ID             = 0x0028;
     70    public static final int TAG_RAW_DATA_OFFSET             = 0x0081;
     71    public static final int TAG_ORIGINAL_DECISION_DATA_OFFSET = 0x0083;
     72    public static final int TAG_CUSTOM_FUNCTIONS_1D_ARRAY   = 0x0090; // not currently decoded
     73    public static final int TAG_PERSONAL_FUNCTIONS_ARRAY    = 0x0091; // not currently decoded
     74    public static final int TAG_PERSONAL_FUNCTION_VALUES_ARRAY = 0x0092; // not currently decoded
     75    public static final int TAG_FILE_INFO_ARRAY             = 0x0093; // not currently decoded
     76    public static final int TAG_AF_POINTS_IN_FOCUS_1D       = 0x0094;
     77    public static final int TAG_LENS_MODEL                  = 0x0095;
     78    public static final int TAG_SERIAL_INFO_ARRAY           = 0x0096; // not currently decoded
     79    public static final int TAG_DUST_REMOVAL_DATA           = 0x0097;
     80    public static final int TAG_CROP_INFO                   = 0x0098; // not currently decoded
     81    public static final int TAG_CUSTOM_FUNCTIONS_ARRAY_2    = 0x0099; // not currently decoded
     82    public static final int TAG_ASPECT_INFO_ARRAY           = 0x009A; // not currently decoded
     83    public static final int TAG_PROCESSING_INFO_ARRAY       = 0x00A0; // not currently decoded
     84    public static final int TAG_TONE_CURVE_TABLE            = 0x00A1;
     85    public static final int TAG_SHARPNESS_TABLE             = 0x00A2;
     86    public static final int TAG_SHARPNESS_FREQ_TABLE        = 0x00A3;
     87    public static final int TAG_WHITE_BALANCE_TABLE         = 0x00A4;
     88    public static final int TAG_COLOR_BALANCE_ARRAY         = 0x00A9; // not currently decoded
     89    public static final int TAG_MEASURED_COLOR_ARRAY        = 0x00AA; // not currently decoded
     90    public static final int TAG_COLOR_TEMPERATURE           = 0x00AE;
     91    public static final int TAG_CANON_FLAGS_ARRAY           = 0x00B0; // not currently decoded
     92    public static final int TAG_MODIFIED_INFO_ARRAY         = 0x00B1; // not currently decoded
     93    public static final int TAG_TONE_CURVE_MATCHING         = 0x00B2;
     94    public static final int TAG_WHITE_BALANCE_MATCHING      = 0x00B3;
     95    public static final int TAG_COLOR_SPACE                 = 0x00B4;
     96    public static final int TAG_PREVIEW_IMAGE_INFO_ARRAY    = 0x00B6; // not currently decoded
     97    public static final int TAG_VRD_OFFSET                  = 0x00D0;
     98    public static final int TAG_SENSOR_INFO_ARRAY           = 0x00E0; // not currently decoded
     99    public static final int TAG_COLOR_DATA_ARRAY_2 = 0x4001; // depends upon camera model, not currently decoded
     100    public static final int TAG_COLOR_INFO_ARRAY_2          = 0x4003; // not currently decoded
     101    public static final int TAG_CUSTOM_PICTURE_STYLE_FILE_NAME = 0x4010;
     102    public static final int TAG_COLOR_INFO_ARRAY            = 0x4013; // not currently decoded
     103    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_1 = 0x4015; // not currently decoded
     104    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_2 = 0x4016; // not currently decoded
     105    public static final int TAG_LIGHTING_OPTIMIZER_ARRAY = 0x4018; // not currently decoded
     106    public static final int TAG_LENS_INFO_ARRAY             = 0x4019; // not currently decoded
     107    public static final int TAG_AMBIANCE_INFO_ARRAY         = 0x4020; // not currently decoded
     108    public static final int TAG_FILTER_INFO_ARRAY           = 0x4024; // not currently decoded
    194109
    195     /**
    196      * 0 = Auto
    197      * 1 = Sunny
    198      * 2 = Cloudy
    199      * 3 = Tungsten
    200      * 4 = Flourescent
    201      * 5 = Flash
    202      * 6 = Custom
    203      */
    204     public static final int TAG_CANON_STATE2_WHITE_BALANCE = 0xC207;
    205     public static final int TAG_CANON_STATE2_SEQUENCE_NUMBER = 0xC209;
    206     public static final int TAG_CANON_STATE2_AF_POINT_USED = 0xC20E;
    207     /**
    208      * The value of this tag may be translated into a flash bias value, in EV.
    209      *
    210      * 0xffc0 = -2 EV
    211      * 0xffcc = -1.67 EV
    212      * 0xffd0 = -1.5 EV
    213      * 0xffd4 = -1.33 EV
    214      * 0xffe0 = -1 EV
    215      * 0xffec = -0.67 EV
    216      * 0xfff0 = -0.5 EV
    217      * 0xfff4 = -0.33 EV
    218      * 0x0000 = 0 EV
    219      * 0x000c = 0.33 EV
    220      * 0x0010 = 0.5 EV
    221      * 0x0014 = 0.67 EV
    222      * 0x0020 = 1 EV
    223      * 0x002c = 1.33 EV
    224      * 0x0030 = 1.5 EV
    225      * 0x0034 = 1.67 EV
    226      * 0x0040 = 2 EV
    227      */
    228     public static final int TAG_CANON_STATE2_FLASH_BIAS = 0xC20F;
    229     public static final int TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING = 0xC210;
    230     public static final int TAG_CANON_STATE2_AEB_BRACKET_VALUE = 0xC211;
    231     public static final int TAG_CANON_STATE2_SUBJECT_DISTANCE = 0xC213;
     110    public final static class CameraSettings
     111    {
     112        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
     113        private static final int OFFSET = 0xC100;
    232114
    233     /**
    234      * Long Exposure Noise Reduction
    235      * 0 = Off
    236      * 1 = On
    237      */
    238     public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
     115        /**
     116         * 1 = Macro
     117         * 2 = Normal
     118         */
     119        public static final int TAG_MACRO_MODE = OFFSET + 0x01;
     120        public static final int TAG_SELF_TIMER_DELAY = OFFSET + 0x02;
     121        /**
     122         * 2 = Normal
     123         * 3 = Fine
     124         * 5 = Superfine
     125         */
     126        public static final int TAG_QUALITY = OFFSET + 0x03;
     127        /**
     128         * 0 = Flash Not Fired
     129         * 1 = Auto
     130         * 2 = On
     131         * 3 = Red Eye Reduction
     132         * 4 = Slow Synchro
     133         * 5 = Auto + Red Eye Reduction
     134         * 6 = On + Red Eye Reduction
     135         * 16 = External Flash
     136         */
     137        public static final int TAG_FLASH_MODE = OFFSET + 0x04;
     138        /**
     139         * 0 = Single Frame or Timer Mode
     140         * 1 = Continuous
     141         */
     142        public static final int TAG_CONTINUOUS_DRIVE_MODE = OFFSET + 0x05;
     143        public static final int TAG_UNKNOWN_2 = OFFSET + 0x06;
     144        /**
     145         * 0 = One-Shot
     146         * 1 = AI Servo
     147         * 2 = AI Focus
     148         * 3 = Manual Focus
     149         * 4 = Single
     150         * 5 = Continuous
     151         * 6 = Manual Focus
     152         */
     153        public static final int TAG_FOCUS_MODE_1 = OFFSET + 0x07;
     154        public static final int TAG_UNKNOWN_3 = OFFSET + 0x08;
     155        public static final int TAG_UNKNOWN_4 = OFFSET + 0x09;
     156        /**
     157         * 0 = Large
     158         * 1 = Medium
     159         * 2 = Small
     160         */
     161        public static final int TAG_IMAGE_SIZE = OFFSET + 0x0A;
     162        /**
     163         * 0 = Full Auto
     164         * 1 = Manual
     165         * 2 = Landscape
     166         * 3 = Fast Shutter
     167         * 4 = Slow Shutter
     168         * 5 = Night
     169         * 6 = Black & White
     170         * 7 = Sepia
     171         * 8 = Portrait
     172         * 9 = Sports
     173         * 10 = Macro / Close-Up
     174         * 11 = Pan Focus
     175         */
     176        public static final int TAG_EASY_SHOOTING_MODE = OFFSET + 0x0B;
     177        /**
     178         * 0 = No Digital Zoom
     179         * 1 = 2x
     180         * 2 = 4x
     181         */
     182        public static final int TAG_DIGITAL_ZOOM = OFFSET + 0x0C;
     183        /**
     184         * 0 = Normal
     185         * 1 = High
     186         * 65535 = Low
     187         */
     188        public static final int TAG_CONTRAST = OFFSET + 0x0D;
     189        /**
     190         * 0 = Normal
     191         * 1 = High
     192         * 65535 = Low
     193         */
     194        public static final int TAG_SATURATION = OFFSET + 0x0E;
     195        /**
     196         * 0 = Normal
     197         * 1 = High
     198         * 65535 = Low
     199         */
     200        public static final int TAG_SHARPNESS = OFFSET + 0x0F;
     201        /**
     202         * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
     203         * 15 = Auto ISO
     204         * 16 = ISO 50
     205         * 17 = ISO 100
     206         * 18 = ISO 200
     207         * 19 = ISO 400
     208         */
     209        public static final int TAG_ISO = OFFSET + 0x10;
     210        /**
     211         * 3 = Evaluative
     212         * 4 = Partial
     213         * 5 = Centre Weighted
     214         */
     215        public static final int TAG_METERING_MODE = OFFSET + 0x11;
     216        /**
     217         * 0 = Manual
     218         * 1 = Auto
     219         * 3 = Close-up (Macro)
     220         * 8 = Locked (Pan Mode)
     221         */
     222        public static final int TAG_FOCUS_TYPE = OFFSET + 0x12;
     223        /**
     224         * 12288 = None (Manual Focus)
     225         * 12289 = Auto Selected
     226         * 12290 = Right
     227         * 12291 = Centre
     228         * 12292 = Left
     229         */
     230        public static final int TAG_AF_POINT_SELECTED = OFFSET + 0x13;
     231        /**
     232         * 0 = Easy Shooting (See Easy Shooting Mode)
     233         * 1 = Program
     234         * 2 = Tv-Priority
     235         * 3 = Av-Priority
     236         * 4 = Manual
     237         * 5 = A-DEP
     238         */
     239        public static final int TAG_EXPOSURE_MODE = OFFSET + 0x14;
     240        public static final int TAG_UNKNOWN_7 = OFFSET + 0x15;
     241        public static final int TAG_UNKNOWN_8 = OFFSET + 0x16;
     242        public static final int TAG_LONG_FOCAL_LENGTH = OFFSET + 0x17;
     243        public static final int TAG_SHORT_FOCAL_LENGTH = OFFSET + 0x18;
     244        public static final int TAG_FOCAL_UNITS_PER_MM = OFFSET + 0x19;
     245        public static final int TAG_UNKNOWN_9 = OFFSET + 0x1A;
     246        public static final int TAG_UNKNOWN_10 = OFFSET + 0x1B;
     247        /**
     248         * 0 = Flash Did Not Fire
     249         * 1 = Flash Fired
     250         */
     251        public static final int TAG_FLASH_ACTIVITY = OFFSET + 0x1C;
     252        public static final int TAG_FLASH_DETAILS = OFFSET + 0x1D;
     253        public static final int TAG_UNKNOWN_12 = OFFSET + 0x1E;
     254        public static final int TAG_UNKNOWN_13 = OFFSET + 0x1F;
     255        /**
     256         * 0 = Focus Mode: Single
     257         * 1 = Focus Mode: Continuous
     258         */
     259        public static final int TAG_FOCUS_MODE_2 = OFFSET + 0x20;
     260    }
     261   
     262    public final static class FocalLength
     263    {
     264        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    239265
    240     /**
    241      * Shutter/Auto Exposure-lock buttons
    242      * 0 = AF/AE lock
    243      * 1 = AE lock/AF
    244      * 2 = AF/AF lock
    245      * 3 = AE+release/AE+AF
    246      */
    247     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
     266        private static final int OFFSET = 0xC200;
     267       
     268        /**
     269         * 0 = Auto
     270         * 1 = Sunny
     271         * 2 = Cloudy
     272         * 3 = Tungsten
     273         * 4 = Florescent
     274         * 5 = Flash
     275         * 6 = Custom
     276         */
     277        public static final int TAG_WHITE_BALANCE = OFFSET + 0x07;
     278        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 0x09;
     279        public static final int TAG_AF_POINT_USED = OFFSET + 0x0E;
     280        /**
     281         * The value of this tag may be translated into a flash bias value, in EV.
     282         *
     283         * 0xffc0 = -2 EV
     284         * 0xffcc = -1.67 EV
     285         * 0xffd0 = -1.5 EV
     286         * 0xffd4 = -1.33 EV
     287         * 0xffe0 = -1 EV
     288         * 0xffec = -0.67 EV
     289         * 0xfff0 = -0.5 EV
     290         * 0xfff4 = -0.33 EV
     291         * 0x0000 = 0 EV
     292         * 0x000c = 0.33 EV
     293         * 0x0010 = 0.5 EV
     294         * 0x0014 = 0.67 EV
     295         * 0x0020 = 1 EV
     296         * 0x002c = 1.33 EV
     297         * 0x0030 = 1.5 EV
     298         * 0x0034 = 1.67 EV
     299         * 0x0040 = 2 EV
     300         */
     301        public static final int TAG_FLASH_BIAS = OFFSET + 0x0F;
     302        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 0x10;
     303        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 0x11;
     304        public static final int TAG_SUBJECT_DISTANCE = OFFSET + 0x13;
     305    }
     306   
     307    public final static class ShotInfo
     308    {
     309        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    248310
    249     /**
    250      * Mirror lockup
    251      * 0 = Disable
    252      * 1 = Enable
    253      */
    254     public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
     311        private static final int OFFSET = 0xC400;
    255312
    256     /**
    257      * Tv/Av and exposure level
    258      * 0 = 1/2 stop
    259      * 1 = 1/3 stop
    260      */
    261     public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
     313        public static final int TAG_AUTO_ISO = OFFSET + 1;
     314        public static final int TAG_BASE_ISO = OFFSET + 2;
     315        public static final int TAG_MEASURED_EV = OFFSET + 3;
     316        public static final int TAG_TARGET_APERTURE = OFFSET + 4;
     317        public static final int TAG_TARGET_EXPOSURE_TIME = OFFSET + 5;
     318        public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 6;
     319        public static final int TAG_WHITE_BALANCE = OFFSET + 7;
     320        public static final int TAG_SLOW_SHUTTER = OFFSET + 8;
     321        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 9;
     322        public static final int TAG_OPTICAL_ZOOM_CODE = OFFSET + 10;
     323        public static final int TAG_CAMERA_TEMPERATURE = OFFSET + 12;
     324        public static final int TAG_FLASH_GUIDE_NUMBER = OFFSET + 13;
     325        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 14;
     326        public static final int TAG_FLASH_EXPOSURE_BRACKETING = OFFSET + 15;
     327        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 16;
     328        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 17;
     329        public static final int TAG_CONTROL_MODE = OFFSET + 18;
     330        public static final int TAG_FOCUS_DISTANCE_UPPER = OFFSET + 19;
     331        public static final int TAG_FOCUS_DISTANCE_LOWER = OFFSET + 20;
     332        public static final int TAG_F_NUMBER = OFFSET + 21;
     333        public static final int TAG_EXPOSURE_TIME = OFFSET + 22;
     334        public static final int TAG_MEASURED_EV_2 = OFFSET + 23;
     335        public static final int TAG_BULB_DURATION = OFFSET + 24;
     336        public static final int TAG_CAMERA_TYPE = OFFSET + 26;
     337        public static final int TAG_AUTO_ROTATE = OFFSET + 27;
     338        public static final int TAG_ND_FILTER = OFFSET + 28;
     339        public static final int TAG_SELF_TIMER_2 = OFFSET + 29;
     340        public static final int TAG_FLASH_OUTPUT = OFFSET + 33;
     341    }
    262342
    263     /**
    264      * AF-assist light
    265      * 0 = On (Auto)
    266      * 1 = Off
    267      */
    268     public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
     343    public final static class Panorama
     344    {
     345        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    269346
    270     /**
    271      * Shutter speed in Av mode
    272      * 0 = Automatic
    273      * 1 = 1/200 (fixed)
    274      */
    275     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
     347        private static final int OFFSET = 0xC500;
    276348
    277     /**
    278      * Auto-Exposure Bracketting sequence/auto cancellation
    279      * 0 = 0,-,+ / Enabled
    280      * 1 = 0,-,+ / Disabled
    281      * 2 = -,0,+ / Enabled
    282      * 3 = -,0,+ / Disabled
    283      */
    284     public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307;
     349        public static final int TAG_PANORAMA_FRAME_NUMBER = OFFSET + 2;
     350        public static final int TAG_PANORAMA_DIRECTION = OFFSET + 5;
     351    }
    285352
    286     /**
    287      * Shutter Curtain Sync
    288      * 0 = 1st Curtain Sync
    289      * 1 = 2nd Curtain Sync
    290      */
    291     public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
     353    public final static class AFInfo
     354    {
     355        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
    292356
    293     /**
    294      * Lens Auto-Focus stop button Function Switch
    295      * 0 = AF stop
    296      * 1 = Operate AF
    297      * 2 = Lock AE and start timer
    298      */
    299     public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
     357        private static final int OFFSET = 0xD200;
    300358
    301     /**
    302      * Auto reduction of fill flash
    303      * 0 = Enable
    304      * 1 = Disable
    305      */
    306     public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
     359        public static final int TAG_NUM_AF_POINTS = OFFSET;
     360        public static final int TAG_VALID_AF_POINTS = OFFSET + 1;
     361        public static final int TAG_IMAGE_WIDTH = OFFSET + 2;
     362        public static final int TAG_IMAGE_HEIGHT = OFFSET + 3;
     363        public static final int TAG_AF_IMAGE_WIDTH = OFFSET + 4;
     364        public static final int TAG_AF_IMAGE_HEIGHT = OFFSET + 5;
     365        public static final int TAG_AF_AREA_WIDTH = OFFSET + 6;
     366        public static final int TAG_AF_AREA_HEIGHT = OFFSET + 7;
     367        public static final int TAG_AF_AREA_X_POSITIONS = OFFSET + 8;
     368        public static final int TAG_AF_AREA_Y_POSITIONS = OFFSET + 9;
     369        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 10;
     370        public static final int TAG_PRIMARY_AF_POINT_1 = OFFSET + 11;
     371        public static final int TAG_PRIMARY_AF_POINT_2 = OFFSET + 12; // not sure why there are two of these
     372    }
    307373
    308     /**
    309      * Menu button return position
    310      * 0 = Top
    311      * 1 = Previous (volatile)
    312      * 2 = Previous
    313      */
    314     public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
     374//    /**
     375//     * Long Exposure Noise Reduction
     376//     * 0 = Off
     377//     * 1 = On
     378//     */
     379//    public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
     380//
     381//    /**
     382//     * Shutter/Auto Exposure-lock buttons
     383//     * 0 = AF/AE lock
     384//     * 1 = AE lock/AF
     385//     * 2 = AF/AF lock
     386//     * 3 = AE+release/AE+AF
     387//     */
     388//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
     389//
     390//    /**
     391//     * Mirror lockup
     392//     * 0 = Disable
     393//     * 1 = Enable
     394//     */
     395//    public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
     396//
     397//    /**
     398//     * Tv/Av and exposure level
     399//     * 0 = 1/2 stop
     400//     * 1 = 1/3 stop
     401//     */
     402//    public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
     403//
     404//    /**
     405//     * AF-assist light
     406//     * 0 = On (Auto)
     407//     * 1 = Off
     408//     */
     409//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
     410//
     411//    /**
     412//     * Shutter speed in Av mode
     413//     * 0 = Automatic
     414//     * 1 = 1/200 (fixed)
     415//     */
     416//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
     417//
     418//    /**
     419//     * Auto-Exposure Bracketting sequence/auto cancellation
     420//     * 0 = 0,-,+ / Enabled
     421//     * 1 = 0,-,+ / Disabled
     422//     * 2 = -,0,+ / Enabled
     423//     * 3 = -,0,+ / Disabled
     424//     */
     425//    public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307;
     426//
     427//    /**
     428//     * Shutter Curtain Sync
     429//     * 0 = 1st Curtain Sync
     430//     * 1 = 2nd Curtain Sync
     431//     */
     432//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
     433//
     434//    /**
     435//     * Lens Auto-Focus stop button Function Switch
     436//     * 0 = AF stop
     437//     * 1 = Operate AF
     438//     * 2 = Lock AE and start timer
     439//     */
     440//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
     441//
     442//    /**
     443//     * Auto reduction of fill flash
     444//     * 0 = Enable
     445//     * 1 = Disable
     446//     */
     447//    public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
     448//
     449//    /**
     450//     * Menu button return position
     451//     * 0 = Top
     452//     * 1 = Previous (volatile)
     453//     * 2 = Previous
     454//     */
     455//    public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
     456//
     457//    /**
     458//     * SET button function when shooting
     459//     * 0 = Not Assigned
     460//     * 1 = Change Quality
     461//     * 2 = Change ISO Speed
     462//     * 3 = Select Parameters
     463//     */
     464//    public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;
     465//
     466//    /**
     467//     * Sensor cleaning
     468//     * 0 = Disable
     469//     * 1 = Enable
     470//     */
     471//    public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;
    315472
    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;
    331 
    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");
     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");
    384490
    385         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION), "Long Exposure Noise Reduction");
    386         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS), "Shutter/Auto Exposure-lock Buttons");
    387         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP), "Mirror Lockup");
    388         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL), "Tv/Av And Exposure Level");
    389         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT), "AF-Assist Light");
    390         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE), "Shutter Speed in Av Mode");
    391         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING), "Auto-Exposure Bracketting Sequence/Auto Cancellation");
    392         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC), "Shutter Curtain Sync");
    393         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_STOP), "Lens Auto-Focus Stop Button Function Switch");
    394         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION), "Auto Reduction of Fill Flash");
    395         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN), "Menu Button Return Position");
    396         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION), "SET Button Function When Shooting");
    397         _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING), "Sensor Cleaning");
     491        _tagNameMap.put(CameraSettings.TAG_AF_POINT_SELECTED, "AF Point Selected");
     492        _tagNameMap.put(CameraSettings.TAG_CONTINUOUS_DRIVE_MODE, "Continuous Drive Mode");
     493        _tagNameMap.put(CameraSettings.TAG_CONTRAST, "Contrast");
     494        _tagNameMap.put(CameraSettings.TAG_EASY_SHOOTING_MODE, "Easy Shooting Mode");
     495        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode");
     496        _tagNameMap.put(CameraSettings.TAG_FLASH_DETAILS, "Flash Details");
     497        _tagNameMap.put(CameraSettings.TAG_FLASH_MODE, "Flash Mode");
     498        _tagNameMap.put(CameraSettings.TAG_FOCAL_UNITS_PER_MM, "Focal Units per mm");
     499        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_1, "Focus Mode");
     500        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_2, "Focus Mode");
     501        _tagNameMap.put(CameraSettings.TAG_IMAGE_SIZE, "Image Size");
     502        _tagNameMap.put(CameraSettings.TAG_ISO, "Iso");
     503        _tagNameMap.put(CameraSettings.TAG_LONG_FOCAL_LENGTH, "Long Focal Length");
     504        _tagNameMap.put(CameraSettings.TAG_MACRO_MODE, "Macro Mode");
     505        _tagNameMap.put(CameraSettings.TAG_METERING_MODE, "Metering Mode");
     506        _tagNameMap.put(CameraSettings.TAG_SATURATION, "Saturation");
     507        _tagNameMap.put(CameraSettings.TAG_SELF_TIMER_DELAY, "Self Timer Delay");
     508        _tagNameMap.put(CameraSettings.TAG_SHARPNESS, "Sharpness");
     509        _tagNameMap.put(CameraSettings.TAG_SHORT_FOCAL_LENGTH, "Short Focal Length");
     510        _tagNameMap.put(CameraSettings.TAG_QUALITY, "Quality");
     511        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_2, "Unknown Camera Setting 2");
     512        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_3, "Unknown Camera Setting 3");
     513        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_4, "Unknown Camera Setting 4");
     514        _tagNameMap.put(CameraSettings.TAG_DIGITAL_ZOOM, "Digital Zoom");
     515        _tagNameMap.put(CameraSettings.TAG_FOCUS_TYPE, "Focus Type");
     516        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_7, "Unknown Camera Setting 7");
     517        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_8, "Unknown Camera Setting 8");
     518        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_9, "Unknown Camera Setting 9");
     519        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_10, "Unknown Camera Setting 10");
     520        _tagNameMap.put(CameraSettings.TAG_FLASH_ACTIVITY, "Flash Activity");
     521        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_12, "Unknown Camera Setting 12");
     522        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_13, "Unknown Camera Setting 13");
     523
     524        _tagNameMap.put(FocalLength.TAG_WHITE_BALANCE, "White Balance");
     525        _tagNameMap.put(FocalLength.TAG_SEQUENCE_NUMBER, "Sequence Number");
     526        _tagNameMap.put(FocalLength.TAG_AF_POINT_USED, "AF Point Used");
     527        _tagNameMap.put(FocalLength.TAG_FLASH_BIAS, "Flash Bias");
     528        _tagNameMap.put(FocalLength.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
     529        _tagNameMap.put(FocalLength.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
     530        _tagNameMap.put(FocalLength.TAG_SUBJECT_DISTANCE, "Subject Distance");
     531
     532        _tagNameMap.put(ShotInfo.TAG_AUTO_ISO, "Auto ISO");
     533        _tagNameMap.put(ShotInfo.TAG_BASE_ISO, "Base ISO");
     534        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV, "Measured EV");
     535        _tagNameMap.put(ShotInfo.TAG_TARGET_APERTURE, "Target Aperture");
     536        _tagNameMap.put(ShotInfo.TAG_TARGET_EXPOSURE_TIME, "Target Exposure Time");
     537        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_COMPENSATION, "Exposure Compensation");
     538        _tagNameMap.put(ShotInfo.TAG_WHITE_BALANCE, "White Balance");
     539        _tagNameMap.put(ShotInfo.TAG_SLOW_SHUTTER, "Slow Shutter");
     540        _tagNameMap.put(ShotInfo.TAG_SEQUENCE_NUMBER, "Sequence Number");
     541        _tagNameMap.put(ShotInfo.TAG_OPTICAL_ZOOM_CODE, "Optical Zoom Code");
     542        _tagNameMap.put(ShotInfo.TAG_CAMERA_TEMPERATURE, "Camera Temperature");
     543        _tagNameMap.put(ShotInfo.TAG_FLASH_GUIDE_NUMBER, "Flash Guide Number");
     544        _tagNameMap.put(ShotInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus");
     545        _tagNameMap.put(ShotInfo.TAG_FLASH_EXPOSURE_BRACKETING, "Flash Exposure Compensation");
     546        _tagNameMap.put(ShotInfo.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
     547        _tagNameMap.put(ShotInfo.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
     548        _tagNameMap.put(ShotInfo.TAG_CONTROL_MODE, "Control Mode");
     549        _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_UPPER, "Focus Distance Upper");
     550        _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_LOWER, "Focus Distance Lower");
     551        _tagNameMap.put(ShotInfo.TAG_F_NUMBER, "F Number");
     552        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_TIME, "Exposure Time");
     553        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV_2, "Measured EV 2");
     554        _tagNameMap.put(ShotInfo.TAG_BULB_DURATION, "Bulb Duration");
     555        _tagNameMap.put(ShotInfo.TAG_CAMERA_TYPE, "Camera Type");
     556        _tagNameMap.put(ShotInfo.TAG_AUTO_ROTATE, "Auto Rotate");
     557        _tagNameMap.put(ShotInfo.TAG_ND_FILTER, "ND Filter");
     558        _tagNameMap.put(ShotInfo.TAG_SELF_TIMER_2, "Self Timer 2");
     559        _tagNameMap.put(ShotInfo.TAG_FLASH_OUTPUT, "Flash Output");
     560
     561        _tagNameMap.put(Panorama.TAG_PANORAMA_FRAME_NUMBER, "Panorama Frame Number");
     562        _tagNameMap.put(Panorama.TAG_PANORAMA_DIRECTION, "Panorama Direction");
     563
     564        _tagNameMap.put(AFInfo.TAG_NUM_AF_POINTS, "AF Point Count");
     565        _tagNameMap.put(AFInfo.TAG_VALID_AF_POINTS, "Valid AF Point Count");
     566        _tagNameMap.put(AFInfo.TAG_IMAGE_WIDTH, "Image Width");
     567        _tagNameMap.put(AFInfo.TAG_IMAGE_HEIGHT, "Image Height");
     568        _tagNameMap.put(AFInfo.TAG_AF_IMAGE_WIDTH, "AF Image Width");
     569        _tagNameMap.put(AFInfo.TAG_AF_IMAGE_HEIGHT, "AF Image Height");
     570        _tagNameMap.put(AFInfo.TAG_AF_AREA_WIDTH, "AF Area Width");
     571        _tagNameMap.put(AFInfo.TAG_AF_AREA_HEIGHT, "AF Area Height");
     572        _tagNameMap.put(AFInfo.TAG_AF_AREA_X_POSITIONS, "AF Area X Positions");
     573        _tagNameMap.put(AFInfo.TAG_AF_AREA_Y_POSITIONS, "AF Area Y Positions");
     574        _tagNameMap.put(AFInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus Count");
     575        _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_1, "Primary AF Point 1");
     576        _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_2, "Primary AF Point 2");
     577       
     578//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction");
     579//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS, "Shutter/Auto Exposure-lock Buttons");
     580//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP, "Mirror Lockup");
     581//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL, "Tv/Av And Exposure Level");
     582//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT, "AF-Assist Light");
     583//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE, "Shutter Speed in Av Mode");
     584//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING, "Auto-Exposure Bracketting Sequence/Auto Cancellation");
     585//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC, "Shutter Curtain Sync");
     586//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_STOP, "Lens Auto-Focus Stop Button Function Switch");
     587//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION, "Auto Reduction of Fill Flash");
     588//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN, "Menu Button Return Position");
     589//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION, "SET Button Function When Shooting");
     590//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING, "Sensor Cleaning");
     591
     592        _tagNameMap.put(TAG_THUMBNAIL_IMAGE_VALID_AREA, "Thumbnail Image Valid Area");
     593        _tagNameMap.put(TAG_SERIAL_NUMBER_FORMAT, "Serial Number Format");
     594        _tagNameMap.put(TAG_SUPER_MACRO, "Super Macro");
     595        _tagNameMap.put(TAG_DATE_STAMP_MODE, "Date Stamp Mode");
     596        _tagNameMap.put(TAG_MY_COLORS, "My Colors");
     597        _tagNameMap.put(TAG_FIRMWARE_REVISION, "Firmware Revision");
     598        _tagNameMap.put(TAG_CATEGORIES, "Categories");
     599        _tagNameMap.put(TAG_FACE_DETECT_ARRAY_1, "Face Detect Array 1");
     600        _tagNameMap.put(TAG_FACE_DETECT_ARRAY_2, "Face Detect Array 2");
     601        _tagNameMap.put(TAG_AF_INFO_ARRAY_2, "AF Info Array 2");
     602        _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Image Unique ID");
     603        _tagNameMap.put(TAG_RAW_DATA_OFFSET, "Raw Data Offset");
     604        _tagNameMap.put(TAG_ORIGINAL_DECISION_DATA_OFFSET, "Original Decision Data Offset");
     605        _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_1D_ARRAY, "Custom Functions (1D) Array");
     606        _tagNameMap.put(TAG_PERSONAL_FUNCTIONS_ARRAY, "Personal Functions Array");
     607        _tagNameMap.put(TAG_PERSONAL_FUNCTION_VALUES_ARRAY, "Personal Function Values Array");
     608        _tagNameMap.put(TAG_FILE_INFO_ARRAY, "File Info Array");
     609        _tagNameMap.put(TAG_AF_POINTS_IN_FOCUS_1D, "AF Points in Focus (1D)");
     610        _tagNameMap.put(TAG_LENS_MODEL, "Lens Model");
     611        _tagNameMap.put(TAG_SERIAL_INFO_ARRAY, "Serial Info Array");
     612        _tagNameMap.put(TAG_DUST_REMOVAL_DATA, "Dust Removal Data");
     613        _tagNameMap.put(TAG_CROP_INFO, "Crop Info");
     614        _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_ARRAY_2, "Custom Functions Array 2");
     615        _tagNameMap.put(TAG_ASPECT_INFO_ARRAY, "Aspect Information Array");
     616        _tagNameMap.put(TAG_PROCESSING_INFO_ARRAY, "Processing Information Array");
     617        _tagNameMap.put(TAG_TONE_CURVE_TABLE, "Tone Curve Table");
     618        _tagNameMap.put(TAG_SHARPNESS_TABLE, "Sharpness Table");
     619        _tagNameMap.put(TAG_SHARPNESS_FREQ_TABLE, "Sharpness Frequency Table");
     620        _tagNameMap.put(TAG_WHITE_BALANCE_TABLE, "White Balance Table");
     621        _tagNameMap.put(TAG_COLOR_BALANCE_ARRAY, "Color Balance Array");
     622        _tagNameMap.put(TAG_MEASURED_COLOR_ARRAY, "Measured Color Array");
     623        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
     624        _tagNameMap.put(TAG_CANON_FLAGS_ARRAY, "Canon Flags Array");
     625        _tagNameMap.put(TAG_MODIFIED_INFO_ARRAY, "Modified Information Array");
     626        _tagNameMap.put(TAG_TONE_CURVE_MATCHING, "Tone Curve Matching");
     627        _tagNameMap.put(TAG_WHITE_BALANCE_MATCHING, "White Balance Matching");
     628        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
     629        _tagNameMap.put(TAG_PREVIEW_IMAGE_INFO_ARRAY, "Preview Image Info Array");
     630        _tagNameMap.put(TAG_VRD_OFFSET, "VRD Offset");
     631        _tagNameMap.put(TAG_SENSOR_INFO_ARRAY, "Sensor Information Array");
     632        _tagNameMap.put(TAG_COLOR_DATA_ARRAY_2, "Color Data Array 1");
     633        _tagNameMap.put(TAG_COLOR_INFO_ARRAY_2, "Color Data Array 2");
     634        _tagNameMap.put(TAG_CUSTOM_PICTURE_STYLE_FILE_NAME, "Custom Picture Style File Name");
     635        _tagNameMap.put(TAG_COLOR_INFO_ARRAY, "Color Info Array");
     636        _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_1, "Vignetting Correction Array 1");
     637        _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_2, "Vignetting Correction Array 2");
     638        _tagNameMap.put(TAG_LIGHTING_OPTIMIZER_ARRAY, "Lighting Optimizer Array");
     639        _tagNameMap.put(TAG_LENS_INFO_ARRAY, "Lens Info Array");
     640        _tagNameMap.put(TAG_AMBIANCE_INFO_ARRAY, "Ambiance Info Array");
     641        _tagNameMap.put(TAG_FILTER_INFO_ARRAY, "Filter Info Array");
    398642    }
    399643
    400644    public CanonMakernoteDirectory()
     
    402646        this.setDescriptor(new CanonMakernoteDescriptor(this));
    403647    }
    404648
     649    @NotNull
    405650    public String getName()
    406651    {
    407652        return "Canon Makernote";
    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)
     661    @Override
     662    public void setIntArray(int tagType, @NotNull int[] ints)
    421663    {
    422         if (tagType == TAG_CANON_CAMERA_STATE_1) {
    423             // this single tag has multiple values within
    424             int subTagTypeBase = 0xC100;
    425             // we intentionally skip the first array member
    426             for (int i = 1; i < ints.length; i++) {
    427                 setInt(subTagTypeBase + i, ints[i]);
    428             }
    429         } else if (tagType == TAG_CANON_CAMERA_STATE_2) {
    430             // this single tag has multiple values within
    431             int subTagTypeBase = 0xC200;
    432             // we intentionally skip the first array member
    433             for (int i = 1; i < ints.length; i++) {
    434                 setInt(subTagTypeBase + i, ints[i]);
    435             }
    436         } if (tagType == TAG_CANON_CUSTOM_FUNCTIONS) {
    437             // this single tag has multiple values within
    438             int subTagTypeBase = 0xC300;
    439             // we intentionally skip the first array member
    440             for (int i = 1; i < ints.length; i++) {
    441                 setInt(subTagTypeBase + i + 1, ints[i] & 0x0F);
    442             }
    443         } else {
    444             // no special handling...
    445             super.setIntArray(tagType, ints);
     664        // TODO is there some way to drop out 'null' or 'zero' values that are present in the array to reduce the noise?
     665       
     666        // Certain Canon tags contain arrays of values that we split into 'fake' tags as each
     667        // index in the array has its own meaning and decoding.
     668        // Pick those tags out here and throw away the original array.
     669        // Otherwise just add as usual.
     670        switch (tagType) {
     671            case TAG_CAMERA_SETTINGS_ARRAY:
     672                for (int i = 0; i < ints.length; i++)
     673                    setInt(CameraSettings.OFFSET + i, ints[i]);
     674                break;
     675            case TAG_FOCAL_LENGTH_ARRAY:
     676                for (int i = 0; i < ints.length; i++)
     677                    setInt(FocalLength.OFFSET + i, ints[i]);
     678                break;
     679            case TAG_SHOT_INFO_ARRAY:
     680                for (int i = 0; i < ints.length; i++)
     681                    setInt(ShotInfo.OFFSET + i, ints[i]);
     682                break;
     683            case TAG_PANORAMA_ARRAY:
     684                for (int i = 0; i < ints.length; i++)
     685                    setInt(Panorama.OFFSET + i, ints[i]);
     686                break;
     687            // TODO the interpretation of the custom functions tag depends upon the camera model
     688//            case TAG_CANON_CUSTOM_FUNCTIONS_ARRAY:
     689//                int subTagTypeBase = 0xC300;
     690//                // we intentionally skip the first array member
     691//                for (int i = 1; i < ints.length; i++)
     692//                    setInt(subTagTypeBase + i + 1, ints[i] & 0x0F);
     693//                break;
     694            case TAG_AF_INFO_ARRAY:
     695                for (int i = 0; i < ints.length; i++)
     696                    setInt(AFInfo.OFFSET + i, ints[i]);
     697                break;
     698            default:
     699                // no special handling...
     700                super.setIntArray(tagType, ints);
     701                break;
    446702        }
    447703    }
    448704}
  • src/com/drew/metadata/exif/CasioType1MakernoteDescriptor.java

     
    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>CasioType1MakernoteDirectory</code>.
    2429 *
     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) {
    3643            case CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE:
     
    5865            case CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY:
    5966                return getCcdSensitivityDescription();
    6067            default:
    61                 return _directory.getString(tagType);
     68                return super.getDescription(tagType);
    6269        }
    6370    }
    6471
    65     public String getCcdSensitivityDescription() throws MetadataException
     72    @Nullable
     73    public String getCcdSensitivityDescription()
    6674    {
    67         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY)) return null;
    68         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY);
     75        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY);
     76
     77        if (value == null)
     78            return null;
     79
    6980        switch (value) {
    7081            // these four for QV3000
    7182            case 64:
     
    8697        }
    8798    }
    8899
    89     public String getSaturationDescription() throws MetadataException
     100    @Nullable
     101    public String getSaturationDescription()
    90102    {
    91         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION)) return null;
    92         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION);
     103        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION);
     104
     105        if (value == null)
     106            return null;
     107
    93108        switch (value) {
    94109            case 0:
    95110                return "Normal";
     
    102117        }
    103118    }
    104119
    105     public String getContrastDescription() throws MetadataException
     120    @Nullable
     121    public String getContrastDescription()
    106122    {
    107         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST)) return null;
    108         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST);
     123        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST);
     124
     125        if (value == null)
     126            return null;
     127
    109128        switch (value) {
    110129            case 0:
    111130                return "Normal";
     
    118137        }
    119138    }
    120139
    121     public String getSharpnessDescription() throws MetadataException
     140    @Nullable
     141    public String getSharpnessDescription()
    122142    {
    123         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS)) return null;
    124         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS);
     143        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS);
     144
     145        if (value == null)
     146            return null;
     147
    125148        switch (value) {
    126149            case 0:
    127150                return "Normal";
     
    134157        }
    135158    }
    136159
    137     public String getDigitalZoomDescription() throws MetadataException
     160    @Nullable
     161    public String getDigitalZoomDescription()
    138162    {
    139         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM)) return null;
    140         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM);
     163        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM);
     164
     165        if (value == null)
     166            return null;
     167
    141168        switch (value) {
    142169            case 0x10000:
    143170                return "No digital zoom";
     
    152179        }
    153180    }
    154181
    155     public String getWhiteBalanceDescription() throws MetadataException
     182    @Nullable
     183    public String getWhiteBalanceDescription()
    156184    {
    157         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE)) return null;
    158         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE);
     185        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE);
     186
     187        if (value == null)
     188            return null;
     189
    159190        switch (value) {
    160191            case 1:
    161192                return "Auto";
     
    164195            case 3:
    165196                return "Daylight";
    166197            case 4:
    167                 return "Flourescent";
     198                return "Florescent";
    168199            case 5:
    169200                return "Shade";
    170201            case 129:
     
    174205        }
    175206    }
    176207
    177     public String getObjectDistanceDescription() throws MetadataException
     208    @Nullable
     209    public String getObjectDistanceDescription()
    178210    {
    179         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE)) return null;
    180         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE);
     211        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE);
     212
     213        if (value == null)
     214            return null;
     215
    181216        return value + " mm";
    182217    }
    183218
    184     public String getFlashIntensityDescription() throws MetadataException
     219    @Nullable
     220    public String getFlashIntensityDescription()
    185221    {
    186         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY)) return null;
    187         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY);
     222        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY);
     223
     224        if (value == null)
     225            return null;
     226
    188227        switch (value) {
    189228            case 11:
    190229                return "Weak";
     
    197236        }
    198237    }
    199238
    200     public String getFlashModeDescription() throws MetadataException
     239    @Nullable
     240    public String getFlashModeDescription()
    201241    {
    202         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE)) return null;
    203         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE);
     242        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE);
     243
     244        if (value == null)
     245            return null;
     246
    204247        switch (value) {
    205248            case 1:
    206249                return "Auto";
     
    217260        }
    218261    }
    219262
    220     public String getFocusingModeDescription() throws MetadataException
     263    @Nullable
     264    public String getFocusingModeDescription()
    221265    {
    222         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE)) return null;
    223         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE);
     266        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE);
     267
     268        if (value == null)
     269            return null;
     270
    224271        switch (value) {
    225272            case 2:
    226273                return "Macro";
     
    235282        }
    236283    }
    237284
    238     public String getQualityDescription() throws MetadataException
     285    @Nullable
     286    public String getQualityDescription()
    239287    {
    240         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY)) return null;
    241         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY);
     288        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY);
     289
     290        if (value == null)
     291            return null;
     292
    242293        switch (value) {
    243294            case 1:
    244295                return "Economy";
     
    251302        }
    252303    }
    253304
    254     public String getRecordingModeDescription() throws MetadataException
     305    @Nullable
     306    public String getRecordingModeDescription()
    255307    {
    256         if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE)) return null;
    257         int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE);
     308        Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE);
     309
     310        if (value == null)
     311            return null;
     312
    258313        switch (value) {
    259314            case 1:
    260315                return "Single shutter";
  • src/com/drew/metadata/exif/CasioType1MakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    2837{
     
    4756    public static final int TAG_CASIO_UNKNOWN_8 = 0x0013;
    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
    7686    public CasioType1MakernoteDirectory()
     
    7888        this.setDescriptor(new CasioType1MakernoteDescriptor(this));
    7989    }
    8090
     91    @NotNull
    8192    public String getName()
    8293    {
    8394        return "Casio Makernote";
    8495    }
    8596
    86     protected HashMap getTagNameMap()
     97    @NotNull
     98    protected HashMap<Integer, String> getTagNameMap()
    8799    {
    88         return tagNameMap;
     100        return _tagNameMap;
    89101    }
    90102}
  • src/com/drew/metadata/exif/CasioType2MakernoteDescriptor.java

     
    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>CasioType2MakernoteDirectory</code>.
    2429 *
     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) {
    3643            case CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS:
     
    9097            case CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER:
    9198                return getFilterDescription();
    9299            default:
    93                 return _directory.getString(tagType);
     100                return super.getDescription(tagType);
    94101        }
    95102    }
    96103
    97     public String getFilterDescription() throws MetadataException
     104    @Nullable
     105    public String getFilterDescription()
    98106    {
    99         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER)) return null;
    100         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER);
     107        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER);
     108        if (value==null)
     109            return null;
    101110        switch (value) {
    102111            case 0:
    103112                return "Off";
     
    106115        }
    107116    }
    108117
    109     public String getEnhancementDescription() throws MetadataException
     118    @Nullable
     119    public String getEnhancementDescription()
    110120    {
    111         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT)) return null;
    112         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT);
     121        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT);
     122        if (value==null)
     123            return null;
    113124        switch (value) {
    114125            case 0:
    115126                return "Off";
     
    118129        }
    119130    }
    120131
    121     public String getColourModeDescription() throws MetadataException
     132    @Nullable
     133    public String getColourModeDescription()
    122134    {
    123         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE)) return null;
    124         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE);
     135        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE);
     136        if (value==null)
     137            return null;
    125138        switch (value) {
    126139            case 0:
    127140                return "Off";
     
    130143        }
    131144    }
    132145
    133     public String getCcdIsoSensitivityDescription() throws MetadataException
     146    @Nullable
     147    public String getCcdIsoSensitivityDescription()
    134148    {
    135         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY)) return null;
    136         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY);
     149        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY);
     150        if (value==null)
     151            return null;
    137152        switch (value) {
    138153            case 0:
    139154                return "Off";
     
    144159        }
    145160    }
    146161
    147     public String getBestShotModeDescription() throws MetadataException
     162    @Nullable
     163    public String getBestShotModeDescription()
    148164    {
    149         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE)) return null;
    150         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE);
     165        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE);
     166        if (value==null)
     167            return null;
    151168        switch (value) {
    152169            default:
    153170                return "Unknown (" + value + ")";
    154171        }
    155172    }
    156173
     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
     180    @Nullable
     181    public String getFocusMode2Description()
    164182    {
    165         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2)) return null;
    166         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2);
     183        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2);
     184        if (value==null)
     185            return null;
    167186        switch (value) {
    168187            case 1:
    169188                return "Fixation";
     
    174193        }
    175194    }
    176195
    177     public String getQualityDescription() throws MetadataException
     196    @Nullable
     197    public String getQualityDescription()
    178198    {
    179         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY)) return null;
    180         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY);
     199        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY);
     200        if (value==null)
     201            return null;
    181202        switch (value) {
    182203            case 3:
    183204                return "Fine";
     
    186207        }
    187208    }
    188209
    189     public String getSelfTimerDescription() throws MetadataException
     210    @Nullable
     211    public String getSelfTimerDescription()
    190212    {
    191         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER)) return null;
    192         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER);
     213        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER);
     214        if (value==null)
     215            return null;
    193216        switch (value) {
    194217            case 1:
    195218                return "Off";
     
    198221        }
    199222    }
    200223
    201     public String getRecordModeDescription() throws MetadataException
     224    @Nullable
     225    public String getRecordModeDescription()
    202226    {
    203         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE)) return null;
    204         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE);
     227        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE);
     228        if (value==null)
     229            return null;
    205230        switch (value) {
    206231            case 2:
    207232                return "Normal";
     
    210235        }
    211236    }
    212237
    213     public String getFlashDistanceDescription() throws MetadataException
     238    @Nullable
     239    public String getFlashDistanceDescription()
    214240    {
    215         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE)) return null;
    216         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE);
     241        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE);
     242        if (value==null)
     243            return null;
    217244        switch (value) {
    218245            case 0:
    219246                return "Off";
     
    222249        }
    223250    }
    224251
    225     public String getObjectDistanceDescription() throws MetadataException
     252    @Nullable
     253    public String getObjectDistanceDescription()
    226254    {
    227         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE)) return null;
    228         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE);
     255        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE);
     256        if (value==null)
     257            return null;
    229258        return Integer.toString(value) + " mm";
    230259    }
    231260
    232     public String getWhiteBalance2Description() throws MetadataException
     261    @Nullable
     262    public String getWhiteBalance2Description()
    233263    {
    234         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2)) return null;
    235         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2);
     264        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2);
     265        if (value==null)
     266            return null;
    236267        switch (value) {
    237268            case 0:
    238269                return "Manual";
     
    247278        }
    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
     287    @Nullable
     288    public String getCasioPreviewThumbnailDescription()
    257289    {
    258         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL)) return null;
    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
     303    @Nullable
     304    public String getSharpnessDescription()
    271305    {
    272         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS)) return null;
    273         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS);
     306        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS);
     307        if (value==null)
     308            return null;
    274309        switch (value) {
    275310            case 0:
    276311                return "-1";
     
    283318        }
    284319    }
    285320
    286     public String getContrastDescription() throws MetadataException
     321    @Nullable
     322    public String getContrastDescription()
    287323    {
    288         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST)) return null;
    289         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST);
     324        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST);
     325        if (value==null)
     326            return null;
    290327        switch (value) {
    291328            case 0:
    292329                return "-1";
     
    299336        }
    300337    }
    301338
    302     public String getSaturationDescription() throws MetadataException
     339    @Nullable
     340    public String getSaturationDescription()
    303341    {
    304         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION)) return null;
    305         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION);
     342        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION);
     343        if (value==null)
     344            return null;
    306345        switch (value) {
    307346            case 0:
    308347                return "-1";
     
    315354        }
    316355    }
    317356
    318     public String getFocalLengthDescription() throws MetadataException
     357    @Nullable
     358    public String getFocalLengthDescription()
    319359    {
    320         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH)) return null;
    321         double value = _directory.getDouble(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH);
     360        Double value = _directory.getDoubleObject(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH);
     361        if (value==null)
     362            return null;
    322363        return Double.toString(value / 10d) + " mm";
    323364    }
    324365
    325     public String getWhiteBalance1Description() throws MetadataException
     366    @Nullable
     367    public String getWhiteBalance1Description()
    326368    {
    327         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1)) return null;
    328         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1);
     369        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1);
     370        if (value==null)
     371            return null;
    329372        switch (value) {
    330373            case 0:
    331374                return "Auto";
     
    336379            case 3:
    337380                return "Tungsten";
    338381            case 4:
    339                 return "Flourescent";
     382                return "Florescent";
    340383            case 5:
    341384                return "Manual";
    342385            default:
     
    344387        }
    345388    }
    346389
    347     public String getIsoSensitivityDescription() throws MetadataException
     390    @Nullable
     391    public String getIsoSensitivityDescription()
    348392    {
    349         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY)) return null;
    350         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY);
     393        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY);
     394        if (value==null)
     395            return null;
    351396        switch (value) {
    352397            case 3:
    353398                return "50";
     
    362407        }
    363408    }
    364409
    365     public String getFocusMode1Description() throws MetadataException
     410    @Nullable
     411    public String getFocusMode1Description()
    366412    {
    367         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1)) return null;
    368         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1);
     413        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1);
     414        if (value==null)
     415            return null;
    369416        switch (value) {
    370417            case 0:
    371418                return "Normal";
     
    376423        }
    377424    }
    378425
    379     public String getImageSizeDescription() throws MetadataException
     426    @Nullable
     427    public String getImageSizeDescription()
    380428    {
    381         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE)) return null;
    382         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE);
     429        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE);
     430        if (value==null)
     431            return null;
    383432        switch (value) {
    384433            case 0:  return "640 x 480 pixels";
    385434            case 4:  return "1600 x 1200 pixels";
     
    392441        }
    393442    }
    394443
    395     public String getQualityModeDescription() throws MetadataException
     444    @Nullable
     445    public String getQualityModeDescription()
    396446    {
    397         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE)) return null;
    398         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE);
     447        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE);
     448        if (value==null)
     449            return null;
    399450        switch (value) {
    400451            case 1:
    401452                return "Fine";
     
    406457        }
    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
     466    @Nullable
     467    public String getThumbnailSizeDescription()
    416468    {
    417         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE)) return null;
    418         int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE);
     469        Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE);
     470        if (value==null)
     471            return null;
    419472        return Integer.toString(value) + " bytes";
    420473    }
    421474
    422     public String getThumbnailDimensionsDescription() throws MetadataException
     475    @Nullable
     476    public String getThumbnailDimensionsDescription()
    423477    {
    424         if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS)) return null;
    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";
    429482    }
    430 }
    431  Pas de retour chariot à la fin du fichier
     483}
  • src/com/drew/metadata/exif/CasioType2MakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    2837{
     
    166175     */
    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
    204214    public CasioType2MakernoteDirectory()
     
    206216        this.setDescriptor(new CasioType2MakernoteDescriptor(this));
    207217    }
    208218
     219    @NotNull
    209220    public String getName()
    210221    {
    211222        return "Casio Makernote";
    212223    }
    213224
    214     protected HashMap getTagNameMap()
     225    @NotNull
     226    protected HashMap<Integer, String> getTagNameMap()
    215227    {
    216         return tagNameMap;
     228        return _tagNameMap;
    217229    }
    218230}
  • src/com/drew/metadata/exif/DataFormat.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  */
    15 package com.drew.metadata.exif;
    16 
    17 import com.drew.metadata.MetadataException;
    18 
    19 /**
    20  * An enumeration of data formats used in the TIFF IFDs.
    21  */
    22 public class DataFormat
    23 {
    24     public static final DataFormat BYTE = new DataFormat("BYTE", 1);
    25     public static final DataFormat STRING = new DataFormat("STRING", 2);
    26     public static final DataFormat USHORT = new DataFormat("USHORT", 3);
    27     public static final DataFormat ULONG = new DataFormat("ULONG", 4);
    28     public static final DataFormat URATIONAL = new DataFormat("URATIONAL", 5);
    29     public static final DataFormat SBYTE = new DataFormat("SBYTE", 6);
    30     public static final DataFormat UNDEFINED = new DataFormat("UNDEFINED", 7);
    31     public static final DataFormat SSHORT = new DataFormat("SSHORT", 8);
    32     public static final DataFormat SLONG = new DataFormat("SLONG", 9);
    33     public static final DataFormat SRATIONAL = new DataFormat("SRATIONAL", 10);
    34     public static final DataFormat SINGLE = new DataFormat("SINGLE", 11);
    35     public static final DataFormat DOUBLE = new DataFormat("DOUBLE", 12);
    36 
    37     private final String myName;
    38     private final int value;
    39 
    40     public static DataFormat fromValue(int value) throws MetadataException
    41     {
    42         switch (value)
    43         {
    44             case 1:  return BYTE;
    45             case 2:  return STRING;
    46             case 3:  return USHORT;
    47             case 4:  return ULONG;
    48             case 5:  return URATIONAL;
    49             case 6:  return SBYTE;
    50             case 7:  return UNDEFINED;
    51             case 8:  return SSHORT;
    52             case 9:  return SLONG;
    53             case 10: return SRATIONAL;
    54             case 11: return SINGLE;
    55             case 12: return DOUBLE;
    56         }
    57 
    58         throw new MetadataException("value '"+value+"' does not represent a known data format.");
    59     }
    60 
    61     private DataFormat(String name, int value)
    62     {
    63         myName = name;
    64         this.value = value;
    65     }
    66 
    67     public int getValue()
    68     {
    69         return value;
    70     }
    71 
    72     public String toString()
    73     {
    74         return myName;
    75     }
    76 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.metadata.MetadataException;
     25
     26/**
     27 * An enumeration of data formats used in the TIFF IFDs.
     28 *
     29 * @author Drew Noakes http://drewnoakes.com
     30 */
     31public class DataFormat
     32{
     33    @NotNull public static final DataFormat BYTE = new DataFormat("BYTE", 1);
     34    @NotNull public static final DataFormat STRING = new DataFormat("STRING", 2);
     35    @NotNull public static final DataFormat USHORT = new DataFormat("USHORT", 3);
     36    @NotNull public static final DataFormat ULONG = new DataFormat("ULONG", 4);
     37    @NotNull public static final DataFormat URATIONAL = new DataFormat("URATIONAL", 5);
     38    @NotNull public static final DataFormat SBYTE = new DataFormat("SBYTE", 6);
     39    @NotNull public static final DataFormat UNDEFINED = new DataFormat("UNDEFINED", 7);
     40    @NotNull public static final DataFormat SSHORT = new DataFormat("SSHORT", 8);
     41    @NotNull public static final DataFormat SLONG = new DataFormat("SLONG", 9);
     42    @NotNull public static final DataFormat SRATIONAL = new DataFormat("SRATIONAL", 10);
     43    @NotNull public static final DataFormat SINGLE = new DataFormat("SINGLE", 11);
     44    @NotNull public static final DataFormat DOUBLE = new DataFormat("DOUBLE", 12);
     45
     46    @NotNull private final String _name;
     47    private final int _value;
     48
     49    @NotNull
     50    public static DataFormat fromValue(int value) throws MetadataException
     51    {
     52        switch (value)
     53        {
     54            case 1:  return BYTE;
     55            case 2:  return STRING;
     56            case 3:  return USHORT;
     57            case 4:  return ULONG;
     58            case 5:  return URATIONAL;
     59            case 6:  return SBYTE;
     60            case 7:  return UNDEFINED;
     61            case 8:  return SSHORT;
     62            case 9:  return SLONG;
     63            case 10: return SRATIONAL;
     64            case 11: return SINGLE;
     65            case 12: return DOUBLE;
     66        }
     67
     68        throw new MetadataException("value '"+value+"' does not represent a known data format.");
     69    }
     70
     71    private DataFormat(@NotNull String name, int value)
     72    {
     73        _name = name;
     74        _value = value;
     75    }
     76
     77    public int getValue()
     78    {
     79        return _value;
     80    }
     81
     82    @NotNull
     83    public String toString()
     84    {
     85        return _name;
     86    }
     87}
  • src/com/drew/metadata/exif/ExifDescriptor.java

     
    1 /*
    2  * ExifDescriptor.java
    3  *
    4  * This 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.
    8  *
    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.
    11  *
    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/
    16  *
    17  * Created by dnoakes on 12-Nov-2002 22:27:15 using IntelliJ IDEA.
    18  */
    19 package com.drew.metadata.exif;
    20 
    21 import com.drew.imaging.PhotographicConversions;
    22 import com.drew.lang.Rational;
    23 import com.drew.metadata.Directory;
    24 import com.drew.metadata.MetadataException;
    25 import com.drew.metadata.TagDescriptor;
    26 
    27 import java.io.UnsupportedEncodingException;
    28 import java.text.DecimalFormat;
    29 
    30 /**
    31  * Contains all logic for the presentation of raw Exif data, as stored in ExifDirectory.  Use
    32  * this class to provide human-readable descriptions of tag values.
    33  */
    34 public class ExifDescriptor extends TagDescriptor
    35 {
    36     /**
    37      * Dictates whether rational values will be represented in decimal format in instances
    38      * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
    39      */
    40     private boolean _allowDecimalRepresentationOfRationals = true;
    41 
    42     private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#");
    43 
    44     public ExifDescriptor(Directory directory)
    45     {
    46         super(directory);
    47     }
    48 
    49     // Note for the potential addition of brightness presentation in eV:
    50     // Brightness of taken subject. To calculate Exposure(Ev) from BrigtnessValue(Bv),
    51     // you must add SensitivityValue(Sv).
    52     // Ev=BV+Sv   Sv=log2(ISOSpeedRating/3.125)
    53     // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
    54 
    55     /**
    56      * Returns a descriptive value of the the specified tag for this image.
    57      * Where possible, known values will be substituted here in place of the raw
    58      * tokens actually kept in the Exif segment.  If no substitution is
    59      * available, the value provided by getString(int) will be returned.
    60      * @param tagType the tag to find a description for
    61      * @return a description of the image's value for the specified tag, or
    62      *         <code>null</code> if the tag hasn't been defined.
    63      */
    64     public String getDescription(int tagType) throws MetadataException
    65     {
    66         switch (tagType) {
    67             case ExifDirectory.TAG_ORIENTATION:
    68                 return getOrientationDescription();
    69             case ExifDirectory.TAG_NEW_SUBFILE_TYPE:
    70                 return getNewSubfileTypeDescription();
    71             case ExifDirectory.TAG_SUBFILE_TYPE:
    72                 return getSubfileTypeDescription();
    73             case ExifDirectory.TAG_THRESHOLDING:
    74                 return getThresholdingDescription();
    75             case ExifDirectory.TAG_FILL_ORDER:
    76                 return getFillOrderDescription();
    77             case ExifDirectory.TAG_RESOLUTION_UNIT:
    78                 return getResolutionDescription();
    79             case ExifDirectory.TAG_YCBCR_POSITIONING:
    80                 return getYCbCrPositioningDescription();
    81             case ExifDirectory.TAG_EXPOSURE_TIME:
    82                 return getExposureTimeDescription();
    83             case ExifDirectory.TAG_SHUTTER_SPEED:
    84                 return getShutterSpeedDescription();
    85             case ExifDirectory.TAG_FNUMBER:
    86                 return getFNumberDescription();
    87             case ExifDirectory.TAG_X_RESOLUTION:
    88                 return getXResolutionDescription();
    89             case ExifDirectory.TAG_Y_RESOLUTION:
    90                 return getYResolutionDescription();
    91             case ExifDirectory.TAG_THUMBNAIL_OFFSET:
    92                 return getThumbnailOffsetDescription();
    93             case ExifDirectory.TAG_THUMBNAIL_LENGTH:
    94                 return getThumbnailLengthDescription();
    95             case ExifDirectory.TAG_COMPRESSION_LEVEL:
    96                 return getCompressionLevelDescription();
    97             case ExifDirectory.TAG_SUBJECT_DISTANCE:
    98                 return getSubjectDistanceDescription();
    99             case ExifDirectory.TAG_METERING_MODE:
    100                 return getMeteringModeDescription();
    101             case ExifDirectory.TAG_WHITE_BALANCE:
    102                 return getWhiteBalanceDescription();
    103             case ExifDirectory.TAG_FLASH:
    104                 return getFlashDescription();
    105             case ExifDirectory.TAG_FOCAL_LENGTH:
    106                 return getFocalLengthDescription();
    107             case ExifDirectory.TAG_COLOR_SPACE:
    108                 return getColorSpaceDescription();
    109             case ExifDirectory.TAG_EXIF_IMAGE_WIDTH:
    110                 return getExifImageWidthDescription();
    111             case ExifDirectory.TAG_EXIF_IMAGE_HEIGHT:
    112                 return getExifImageHeightDescription();
    113             case ExifDirectory.TAG_FOCAL_PLANE_UNIT:
    114                 return getFocalPlaneResolutionUnitDescription();
    115             case ExifDirectory.TAG_FOCAL_PLANE_X_RES:
    116                 return getFocalPlaneXResolutionDescription();
    117             case ExifDirectory.TAG_FOCAL_PLANE_Y_RES:
    118                 return getFocalPlaneYResolutionDescription();
    119             case ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH:
    120                 return getThumbnailImageWidthDescription();
    121             case ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT:
    122                 return getThumbnailImageHeightDescription();
    123             case ExifDirectory.TAG_BITS_PER_SAMPLE:
    124                 return getBitsPerSampleDescription();
    125             case ExifDirectory.TAG_COMPRESSION:
    126                 return getCompressionDescription();
    127             case ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION:
    128                 return getPhotometricInterpretationDescription();
    129             case ExifDirectory.TAG_ROWS_PER_STRIP:
    130                 return getRowsPerStripDescription();
    131             case ExifDirectory.TAG_STRIP_BYTE_COUNTS:
    132                 return getStripByteCountsDescription();
    133             case ExifDirectory.TAG_SAMPLES_PER_PIXEL:
    134                 return getSamplesPerPixelDescription();
    135             case ExifDirectory.TAG_PLANAR_CONFIGURATION:
    136                 return getPlanarConfigurationDescription();
    137             case ExifDirectory.TAG_YCBCR_SUBSAMPLING:
    138                 return getYCbCrSubsamplingDescription();
    139             case ExifDirectory.TAG_EXPOSURE_PROGRAM:
    140                 return getExposureProgramDescription();
    141             case ExifDirectory.TAG_APERTURE:
    142                 return getApertureValueDescription();
    143             case ExifDirectory.TAG_MAX_APERTURE:
    144                 return getMaxApertureValueDescription();
    145             case ExifDirectory.TAG_SENSING_METHOD:
    146                 return getSensingMethodDescription();
    147             case ExifDirectory.TAG_EXPOSURE_BIAS:
    148                 return getExposureBiasDescription();
    149             case ExifDirectory.TAG_FILE_SOURCE:
    150                 return getFileSourceDescription();
    151             case ExifDirectory.TAG_SCENE_TYPE:
    152                 return getSceneTypeDescription();
    153             case ExifDirectory.TAG_COMPONENTS_CONFIGURATION:
    154                 return getComponentConfigurationDescription();
    155             case ExifDirectory.TAG_EXIF_VERSION:
    156                 return getExifVersionDescription();
    157             case ExifDirectory.TAG_FLASHPIX_VERSION:
    158                 return getFlashPixVersionDescription();
    159             case ExifDirectory.TAG_REFERENCE_BLACK_WHITE:
    160                 return getReferenceBlackWhiteDescription();
    161             case ExifDirectory.TAG_ISO_EQUIVALENT:
    162                 return getIsoEquivalentDescription();
    163             case ExifDirectory.TAG_THUMBNAIL_DATA:
    164                 return getThumbnailDescription();
    165             case ExifDirectory.TAG_USER_COMMENT:
    166                 return getUserCommentDescription();
    167             case ExifDirectory.TAG_CUSTOM_RENDERED:
    168                 return getCustomRenderedDescription();
    169             case ExifDirectory.TAG_EXPOSURE_MODE:
    170                 return getExposureModeDescription();
    171             case ExifDirectory.TAG_WHITE_BALANCE_MODE:
    172                 return getWhiteBalanceModeDescription();
    173             case ExifDirectory.TAG_DIGITAL_ZOOM_RATIO:
    174                 return getDigitalZoomRatioDescription();
    175             case ExifDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH:
    176                 return get35mmFilmEquivFocalLengthDescription();
    177             case ExifDirectory.TAG_SCENE_CAPTURE_TYPE:
    178                 return getSceneCaptureTypeDescription();
    179             case ExifDirectory.TAG_GAIN_CONTROL:
    180                 return getGainControlDescription();
    181             case ExifDirectory.TAG_CONTRAST:
    182                 return getContrastDescription();
    183             case ExifDirectory.TAG_SATURATION:
    184                 return getSaturationDescription();
    185             case ExifDirectory.TAG_SHARPNESS:
    186                 return getSharpnessDescription();
    187             case ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE:
    188                 return getSubjectDistanceRangeDescription();
    189 
    190             case ExifDirectory.TAG_WIN_AUTHOR:
    191                return getWindowsAuthorDescription();
    192             case ExifDirectory.TAG_WIN_COMMENT:
    193                return getWindowsCommentDescription();
    194             case ExifDirectory.TAG_WIN_KEYWORDS:
    195                return getWindowsKeywordsDescription();
    196             case ExifDirectory.TAG_WIN_SUBJECT:
    197                return getWindowsSubjectDescription();
    198             case ExifDirectory.TAG_WIN_TITLE:
    199                return getWindowsTitleDescription();
    200             default:
    201                 return _directory.getString(tagType);
    202         }
    203     }
    204 
    205     public String getNewSubfileTypeDescription() throws MetadataException
    206     {
    207         if (!_directory.containsTag(ExifDirectory.TAG_NEW_SUBFILE_TYPE)) return null;
    208         switch (_directory.getInt(ExifDirectory.TAG_NEW_SUBFILE_TYPE)) {
    209             case 1: return "Full-resolution image";
    210             case 2: return "Reduced-resolution image";
    211             case 3: return "Single page of multi-page reduced-resolution image";
    212             case 4: return "Transparency mask";
    213             case 5: return "Transparency mask of reduced-resolution image";
    214             case 6: return "Transparency mask of multi-page image";
    215             case 7: return "Transparency mask of reduced-resolution multi-page image";
    216             default:
    217                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_NEW_SUBFILE_TYPE) + ")";
    218         }
    219     }
    220 
    221     public String getSubfileTypeDescription() throws MetadataException
    222     {
    223         if (!_directory.containsTag(ExifDirectory.TAG_SUBFILE_TYPE)) return null;
    224         switch (_directory.getInt(ExifDirectory.TAG_SUBFILE_TYPE)) {
    225             case 1: return "Full-resolution image";
    226             case 2: return "Reduced-resolution image";
    227             case 3: return "Single page of multi-page image";
    228             default:
    229                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SUBFILE_TYPE) + ")";
    230         }
    231     }
    232 
    233     public String getThresholdingDescription() throws MetadataException
    234     {
    235         if (!_directory.containsTag(ExifDirectory.TAG_THRESHOLDING)) return null;
    236         switch (_directory.getInt(ExifDirectory.TAG_THRESHOLDING)) {
    237             case 1: return "No dithering or halftoning";
    238             case 2: return "Ordered dither or halftone";
    239             case 3: return "Randomized dither";
    240             default:
    241                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_THRESHOLDING) + ")";
    242         }
    243     }
    244 
    245     public String getFillOrderDescription() throws MetadataException
    246     {
    247         if (!_directory.containsTag(ExifDirectory.TAG_FILL_ORDER)) return null;
    248         switch (_directory.getInt(ExifDirectory.TAG_FILL_ORDER)) {
    249             case 1: return "Normal";
    250             case 2: return "Reversed";
    251             default:
    252                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_FILL_ORDER) + ")";
    253         }
    254     }
    255 
    256     public String getSubjectDistanceRangeDescription() throws MetadataException
    257     {
    258         if (!_directory.containsTag(ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE)) return null;
    259         switch (_directory.getInt(ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE)) {
    260             case 0:
    261                 return "Unknown";
    262             case 1:
    263                 return "Macro";
    264             case 2:
    265                 return "Close view";
    266             case 3:
    267                 return "Distant view";
    268             default:
    269                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE) + ")";
    270         }
    271     }
    272 
    273     public String getSharpnessDescription() throws MetadataException
    274     {
    275         if (!_directory.containsTag(ExifDirectory.TAG_SHARPNESS)) return null;
    276         switch (_directory.getInt(ExifDirectory.TAG_SHARPNESS)) {
    277             case 0:
    278                 return "None";
    279             case 1:
    280                 return "Low";
    281             case 2:
    282                 return "Hard";
    283             default:
    284                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SHARPNESS) + ")";
    285         }
    286     }
    287 
    288     public String getSaturationDescription() throws MetadataException
    289     {
    290         if (!_directory.containsTag(ExifDirectory.TAG_SATURATION)) return null;
    291         switch (_directory.getInt(ExifDirectory.TAG_SATURATION)) {
    292             case 0:
    293                 return "None";
    294             case 1:
    295                 return "Low saturation";
    296             case 2:
    297                 return "High saturation";
    298             default:
    299                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SATURATION) + ")";
    300         }
    301     }
    302 
    303     public String getContrastDescription() throws MetadataException
    304     {
    305         if (!_directory.containsTag(ExifDirectory.TAG_CONTRAST)) return null;
    306         switch (_directory.getInt(ExifDirectory.TAG_CONTRAST)) {
    307             case 0:
    308                 return "None";
    309             case 1:
    310                 return "Soft";
    311             case 2:
    312                 return "Hard";
    313             default:
    314                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_CONTRAST) + ")";
    315         }
    316     }
    317 
    318     public String getGainControlDescription() throws MetadataException
    319     {
    320         if (!_directory.containsTag(ExifDirectory.TAG_GAIN_CONTROL)) return null;
    321         switch (_directory.getInt(ExifDirectory.TAG_GAIN_CONTROL)) {
    322             case 0:
    323                 return "None";
    324             case 1:
    325                 return "Low gain up";
    326             case 2:
    327                 return "Low gain down";
    328             case 3:
    329                 return "High gain up";
    330             case 4:
    331                 return "High gain down";
    332             default:
    333                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_GAIN_CONTROL) + ")";
    334         }
    335     }
    336 
    337     public String getSceneCaptureTypeDescription() throws MetadataException
    338     {
    339         if (!_directory.containsTag(ExifDirectory.TAG_SCENE_CAPTURE_TYPE)) return null;
    340         switch (_directory.getInt(ExifDirectory.TAG_SCENE_CAPTURE_TYPE)) {
    341             case 0:
    342                 return "Standard";
    343             case 1:
    344                 return "Landscape";
    345             case 2:
    346                 return "Portrait";
    347             case 3:
    348                 return "Night scene";
    349             default:
    350                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SCENE_CAPTURE_TYPE) + ")";
    351         }
    352     }
    353 
    354     public String get35mmFilmEquivFocalLengthDescription() throws MetadataException
    355     {
    356         if (!_directory.containsTag(ExifDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH)) return null;
    357         int equivalentFocalLength = _directory.getInt(ExifDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH);
    358 
    359         if (equivalentFocalLength==0)
    360             return "Unknown";
    361         else
    362             return SimpleDecimalFormatter.format(equivalentFocalLength) + "mm";
    363     }
    364 
    365     public String getDigitalZoomRatioDescription() throws MetadataException
    366     {
    367         if (!_directory.containsTag(ExifDirectory.TAG_DIGITAL_ZOOM_RATIO)) return null;
    368         Rational rational = _directory.getRational(ExifDirectory.TAG_DIGITAL_ZOOM_RATIO);
    369         if (rational.getNumerator()==0)
    370             return "Digital zoom not used.";
    371 
    372         return SimpleDecimalFormatter.format(rational.doubleValue());
    373     }
    374 
    375     public String getWhiteBalanceModeDescription() throws MetadataException
    376     {
    377         if (!_directory.containsTag(ExifDirectory.TAG_WHITE_BALANCE_MODE)) return null;
    378         switch (_directory.getInt(ExifDirectory.TAG_WHITE_BALANCE_MODE)) {
    379             case 0:
    380                 return "Auto white balance";
    381             case 1:
    382                 return "Manual white balance";
    383             default:
    384                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_WHITE_BALANCE_MODE) + ")";
    385         }
    386     }
    387 
    388     public String getExposureModeDescription() throws MetadataException
    389     {
    390         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_MODE)) return null;
    391         switch (_directory.getInt(ExifDirectory.TAG_EXPOSURE_MODE)) {
    392             case 0:
    393                 return "Auto exposure";
    394             case 1:
    395                 return "Manual exposure";
    396             case 2:
    397                 return "Auto bracket";
    398             default:
    399                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_EXPOSURE_MODE) + ")";
    400         }
    401     }
    402 
    403     public String getCustomRenderedDescription() throws MetadataException
    404     {
    405         if (!_directory.containsTag(ExifDirectory.TAG_CUSTOM_RENDERED)) return null;
    406         switch (_directory.getInt(ExifDirectory.TAG_CUSTOM_RENDERED)) {
    407             case 0:
    408                 return "Normal process";
    409             case 1:
    410                 return "Custom process";
    411             default:
    412                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_CUSTOM_RENDERED) + ")";
    413         }
    414     }
    415 
    416     public String getUserCommentDescription() throws MetadataException
    417     {
    418         if (!_directory.containsTag(ExifDirectory.TAG_USER_COMMENT)) return null;
    419 
    420         byte[] commentBytes = _directory.getByteArray(ExifDirectory.TAG_USER_COMMENT);
    421 
    422         if (commentBytes.length==0)
    423             return "";
    424 
    425         final String[] encodingNames = new String[] { "ASCII", "UNICODE", "JIS" };
    426 
    427         if (commentBytes.length>=10)
    428         {
    429             String encodingRegion = new String(commentBytes, 0, 10);
    430 
    431             // try each encoding name
    432             for (int i = 0; i<encodingNames.length; i++) {
    433                 String encodingName = encodingNames[i];
    434                 if (encodingRegion.startsWith(encodingName))
    435                 {
    436                     // remove the null characters (and any spaces) commonly present after the encoding name
    437                     for (int j = encodingName.length(); j<10; j++) {
    438                         byte b = commentBytes[j];
    439                         if (b!='\0' && b!=' ') {
    440                            if (encodingName.equals("UNICODE")) {
    441                               try {
    442                                  return new String(commentBytes, j, commentBytes.length - j, "UTF-16LE").trim();
    443                               }
    444                               catch (UnsupportedEncodingException ex) {
    445                                  return null;
    446                               }
    447                            }
    448                            return new String(commentBytes, j, commentBytes.length - j).trim();
    449                         }
    450                     }
    451                     return new String(commentBytes, 10, commentBytes.length - 10).trim();
    452                 }
    453             }
    454         }
    455 
    456         // special handling fell through, return a plain string representation
    457         return new String(commentBytes).trim();
    458     }
    459 
    460     public String getThumbnailDescription() throws MetadataException
    461     {
    462         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_DATA)) return null;
    463         int[] thumbnailBytes = _directory.getIntArray(ExifDirectory.TAG_THUMBNAIL_DATA);
    464         return "[" + thumbnailBytes.length + " bytes of thumbnail data]";
    465     }
    466 
    467     public String getIsoEquivalentDescription() throws MetadataException
    468     {
    469         if (!_directory.containsTag(ExifDirectory.TAG_ISO_EQUIVALENT)) return null;
    470         int isoEquiv = _directory.getInt(ExifDirectory.TAG_ISO_EQUIVALENT);
    471         if (isoEquiv < 50) {
    472             isoEquiv *= 200;
    473         }
    474         return Integer.toString(isoEquiv);
    475     }
    476 
    477     public String getReferenceBlackWhiteDescription() throws MetadataException
    478     {
    479         if (!_directory.containsTag(ExifDirectory.TAG_REFERENCE_BLACK_WHITE)) return null;
    480         int[] ints = _directory.getIntArray(ExifDirectory.TAG_REFERENCE_BLACK_WHITE);
    481         int blackR = ints[0];
    482         int whiteR = ints[1];
    483         int blackG = ints[2];
    484         int whiteG = ints[3];
    485         int blackB = ints[4];
    486         int whiteB = ints[5];
    487         String pos = "[" + blackR + "," + blackG + "," + blackB + "] " +
    488                 "[" + whiteR + "," + whiteG + "," + whiteB + "]";
    489         return pos;
    490     }
    491 
    492     public String getExifVersionDescription() throws MetadataException
    493     {
    494         if (!_directory.containsTag(ExifDirectory.TAG_EXIF_VERSION)) return null;
    495         int[] ints = _directory.getIntArray(ExifDirectory.TAG_EXIF_VERSION);
    496         return ExifDescriptor.convertBytesToVersionString(ints);
    497     }
    498 
    499     public String getFlashPixVersionDescription() throws MetadataException
    500     {
    501         if (!_directory.containsTag(ExifDirectory.TAG_FLASHPIX_VERSION)) return null;
    502         int[] ints = _directory.getIntArray(ExifDirectory.TAG_FLASHPIX_VERSION);
    503         return ExifDescriptor.convertBytesToVersionString(ints);
    504     }
    505 
    506     public String getSceneTypeDescription() throws MetadataException
    507     {
    508         if (!_directory.containsTag(ExifDirectory.TAG_SCENE_TYPE)) return null;
    509         int sceneType = _directory.getInt(ExifDirectory.TAG_SCENE_TYPE);
    510         if (sceneType == 1) {
    511             return "Directly photographed image";
    512         } else {
    513             return "Unknown (" + sceneType + ")";
    514         }
    515     }
    516 
    517     public String getFileSourceDescription() throws MetadataException
    518     {
    519         if (!_directory.containsTag(ExifDirectory.TAG_FILE_SOURCE)) return null;
    520         int fileSource = _directory.getInt(ExifDirectory.TAG_FILE_SOURCE);
    521         if (fileSource == 3) {
    522             return "Digital Still Camera (DSC)";
    523         } else {
    524             return "Unknown (" + fileSource + ")";
    525         }
    526     }
    527 
    528     public String getExposureBiasDescription() throws MetadataException
    529     {
    530         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_BIAS)) return null;
    531         Rational exposureBias = _directory.getRational(ExifDirectory.TAG_EXPOSURE_BIAS);
    532         return exposureBias.toSimpleString(true) + " EV";
    533     }
    534 
    535     public String getMaxApertureValueDescription() throws MetadataException
    536     {
    537         if (!_directory.containsTag(ExifDirectory.TAG_MAX_APERTURE)) return null;
    538         double aperture = _directory.getDouble(ExifDirectory.TAG_MAX_APERTURE);
    539         double fStop = PhotographicConversions.apertureToFStop(aperture);
    540         return "F" + SimpleDecimalFormatter.format(fStop);
    541     }
    542 
    543     public String getApertureValueDescription() throws MetadataException
    544     {
    545         if (!_directory.containsTag(ExifDirectory.TAG_APERTURE)) return null;
    546         double aperture = _directory.getDouble(ExifDirectory.TAG_APERTURE);
    547         double fStop = PhotographicConversions.apertureToFStop(aperture);
    548         return "F" + SimpleDecimalFormatter.format(fStop);
    549     }
    550 
    551     public String getExposureProgramDescription() throws MetadataException
    552     {
    553         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_PROGRAM)) return null;
    554         // '1' means manual control, '2' program normal, '3' aperture priority,
    555         // '4' shutter priority, '5' program creative (slow program),
    556         // '6' program action(high-speed program), '7' portrait mode, '8' landscape mode.
    557         switch (_directory.getInt(ExifDirectory.TAG_EXPOSURE_PROGRAM)) {
    558             case 1: return "Manual control";
    559             case 2: return "Program normal";
    560             case 3: return "Aperture priority";
    561             case 4: return "Shutter priority";
    562             case 5: return "Program creative (slow program)";
    563             case 6: return "Program action (high-speed program)";
    564             case 7: return "Portrait mode";
    565             case 8: return "Landscape mode";
    566             default:
    567                 return "Unknown program (" + _directory.getInt(ExifDirectory.TAG_EXPOSURE_PROGRAM) + ")";
    568         }
    569     }
    570 
    571     public String getYCbCrSubsamplingDescription() throws MetadataException
    572     {
    573         if (!_directory.containsTag(ExifDirectory.TAG_YCBCR_SUBSAMPLING)) return null;
    574         int[] positions = _directory.getIntArray(ExifDirectory.TAG_YCBCR_SUBSAMPLING);
    575         if (positions[0] == 2 && positions[1] == 1) {
    576             return "YCbCr4:2:2";
    577         } else if (positions[0] == 2 && positions[1] == 2) {
    578             return "YCbCr4:2:0";
    579         } else {
    580             return "(Unknown)";
    581         }
    582     }
    583 
    584     public String getPlanarConfigurationDescription() throws MetadataException
    585     {
    586         if (!_directory.containsTag(ExifDirectory.TAG_PLANAR_CONFIGURATION)) return null;
    587         // When image format is no compression YCbCr, this value shows byte aligns of YCbCr
    588         // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling
    589         // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr
    590         // plane format.
    591 
    592         switch (_directory.getInt(ExifDirectory.TAG_PLANAR_CONFIGURATION)) {
    593             case 1: return "Chunky (contiguous for each subsampling pixel)";
    594             case 2: return "Separate (Y-plane/Cb-plane/Cr-plane format)";
    595             default:
    596                 return "Unknown configuration";
    597         }
    598     }
    599 
    600     public String getSamplesPerPixelDescription()
    601     {
    602         if (!_directory.containsTag(ExifDirectory.TAG_SAMPLES_PER_PIXEL)) return null;
    603         return _directory.getString(ExifDirectory.TAG_SAMPLES_PER_PIXEL) + " samples/pixel";
    604     }
    605 
    606     public String getRowsPerStripDescription()
    607     {
    608         if (!_directory.containsTag(ExifDirectory.TAG_ROWS_PER_STRIP)) return null;
    609         return _directory.getString(ExifDirectory.TAG_ROWS_PER_STRIP) + " rows/strip";
    610     }
    611 
    612     public String getStripByteCountsDescription()
    613     {
    614         if (!_directory.containsTag(ExifDirectory.TAG_STRIP_BYTE_COUNTS)) return null;
    615         return _directory.getString(ExifDirectory.TAG_STRIP_BYTE_COUNTS) + " bytes";
    616     }
    617 
    618     public String getPhotometricInterpretationDescription() throws MetadataException
    619     {
    620         if (!_directory.containsTag(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION)) return null;
    621         // Shows the color space of the image data components
    622         switch (_directory.getInt(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION)) {
    623             case 0: return "WhiteIsZero";
    624             case 1: return "BlackIsZero";
    625             case 2: return "RGB";
    626             case 3: return "RGB Palette";
    627             case 4: return "Transparency Mask";
    628             case 5: return "CMYK";
    629             case 6: return "YCbCr";
    630             case 8: return "CIELab";
    631             case 9: return "ICCLab";
    632             case 10: return "ITULab";
    633             case 32803: return "Color Filter Array";
    634             case 32844: return "Pixar LogL";
    635             case 32845: return "Pixar LogLuv";
    636             case 32892: return "Linear Raw";
    637             default:
    638                 return "Unknown colour space";
    639         }
    640     }
    641 
    642     public String getCompressionDescription() throws MetadataException
    643     {
    644         if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION)) return null;
    645         switch (_directory.getInt(ExifDirectory.TAG_COMPRESSION)) {
    646             case 1: return "Uncompressed";
    647             case 2: return "CCITT 1D";
    648             case 3: return "T4/Group 3 Fax";
    649             case 4: return "T6/Group 4 Fax";
    650             case 5: return "LZW";
    651             case 6: return "JPEG (old-style)";
    652             case 7: return "JPEG";
    653             case 8: return "Adobe Deflate";
    654             case 9: return "JBIG B&W";
    655             case 10: return "JBIG Color";
    656             case 32766: return "Next";
    657             case 32771: return "CCIRLEW";
    658             case 32773: return "PackBits";
    659             case 32809: return "Thunderscan";
    660             case 32895: return "IT8CTPAD";
    661             case 32896: return "IT8LW";
    662             case 32897: return "IT8MP";
    663             case 32898: return "IT8BL";
    664             case 32908: return "PixarFilm";
    665             case 32909: return "PixarLog";
    666             case 32946: return "Deflate";
    667             case 32947: return "DCS";
    668             case 32661: return "JBIG";
    669             case 32676: return "SGILog";
    670             case 32677: return "SGILog24";
    671             case 32712: return "JPEG 2000";
    672             case 32713: return "Nikon NEF Compressed";
    673             default:
    674                 return "Unknown compression";
    675         }
    676     }
    677 
    678     public String getBitsPerSampleDescription()
    679     {
    680         if (!_directory.containsTag(ExifDirectory.TAG_BITS_PER_SAMPLE)) return null;
    681         return _directory.getString(ExifDirectory.TAG_BITS_PER_SAMPLE) + " bits/component/pixel";
    682     }
    683 
    684     public String getThumbnailImageWidthDescription()
    685     {
    686         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH)) return null;
    687         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH) + " pixels";
    688     }
    689 
    690     public String getThumbnailImageHeightDescription()
    691     {
    692         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT)) return null;
    693         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT) + " pixels";
    694     }
    695 
    696     public String getFocalPlaneXResolutionDescription() throws MetadataException
    697     {
    698         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_X_RES)) return null;
    699         Rational rational = _directory.getRational(ExifDirectory.TAG_FOCAL_PLANE_X_RES);
    700         return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +
    701                 getFocalPlaneResolutionUnitDescription().toLowerCase();
    702     }
    703 
    704     public String getFocalPlaneYResolutionDescription() throws MetadataException
    705     {
    706         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_Y_RES)) return null;
    707         Rational rational = _directory.getRational(ExifDirectory.TAG_FOCAL_PLANE_Y_RES);
    708         return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +
    709                 getFocalPlaneResolutionUnitDescription().toLowerCase();
    710     }
    711 
    712     public String getFocalPlaneResolutionUnitDescription() throws MetadataException
    713     {
    714         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_UNIT)) return null;
    715         // Unit of FocalPlaneXResoluton/FocalPlaneYResolution. '1' means no-unit,
    716         // '2' inch, '3' centimeter.
    717         switch (_directory.getInt(ExifDirectory.TAG_FOCAL_PLANE_UNIT)) {
    718             case 1:
    719                 return "(No unit)";
    720             case 2:
    721                 return "Inches";
    722             case 3:
    723                 return "cm";
    724             default:
    725                 return "";
    726         }
    727     }
    728 
    729     public String getExifImageWidthDescription() throws MetadataException
    730     {
    731         if (!_directory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH)) return null;
    732         return _directory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) + " pixels";
    733     }
    734 
    735     public String getExifImageHeightDescription() throws MetadataException
    736     {
    737         if (!_directory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) return null;
    738         return _directory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT) + " pixels";
    739     }
    740 
    741     public String getColorSpaceDescription() throws MetadataException
    742     {
    743         if (!_directory.containsTag(ExifDirectory.TAG_COLOR_SPACE)) return null;
    744         int colorSpace = _directory.getInt(ExifDirectory.TAG_COLOR_SPACE);
    745         if (colorSpace == 1) {
    746             return "sRGB";
    747         } else if (colorSpace == 65535) {
    748             return "Undefined";
    749         } else {
    750             return "Unknown";
    751         }
    752     }
    753 
    754     public String getFocalLengthDescription() throws MetadataException
    755     {
    756         if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_LENGTH)) return null;
    757         java.text.DecimalFormat formatter = new DecimalFormat("0.0##");
    758         Rational focalLength = _directory.getRational(ExifDirectory.TAG_FOCAL_LENGTH);
    759         return formatter.format(focalLength.doubleValue()) + " mm";
    760     }
    761 
    762     public String getFlashDescription() throws MetadataException
    763     {
    764         if (!_directory.containsTag(ExifDirectory.TAG_FLASH)) return null;
    765 
    766         /*
    767          * This is a bitmask.
    768          * 0 = flash fired
    769          * 1 = return detected
    770          * 2 = return able to be detected
    771          * 3 = unknown
    772          * 4 = auto used
    773          * 5 = unknown
    774          * 6 = red eye reduction used
    775          */
    776 
    777         int val = _directory.getInt(ExifDirectory.TAG_FLASH);
    778 
    779         StringBuffer sb = new StringBuffer();
    780 
    781         if ((val & 0x1)!=0)
    782             sb.append("Flash fired");
    783         else
    784             sb.append("Flash did not fire");
    785 
    786         // check if we're able to detect a return, before we mention it
    787         if ((val & 0x4)!=0)
    788         {
    789             if ((val & 0x2)!=0)
    790                 sb.append(", return detected");
    791             else
    792                 sb.append(", return not detected");
    793         }
    794 
    795         if ((val & 0x10)!=0)
    796             sb.append(", auto");
    797 
    798         if ((val & 0x40)!=0)
    799             sb.append(", red-eye reduction");
    800 
    801         return sb.toString();
    802     }
    803 
    804     public String getWhiteBalanceDescription() throws MetadataException
    805     {
    806         if (!_directory.containsTag(ExifDirectory.TAG_WHITE_BALANCE)) return null;
    807         // '0' means unknown, '1' daylight, '2' fluorescent, '3' tungsten, '10' flash,
    808         // '17' standard light A, '18' standard light B, '19' standard light C, '20' D55,
    809         // '21' D65, '22' D75, '255' other.
    810         switch (_directory.getInt(ExifDirectory.TAG_WHITE_BALANCE)) {
    811             case 0:
    812                 return "Unknown";
    813             case 1:
    814                 return "Daylight";
    815             case 2:
    816                 return "Flourescent";
    817             case 3:
    818                 return "Tungsten";
    819             case 10:
    820                 return "Flash";
    821             case 17:
    822                 return "Standard light";
    823             case 18:
    824                 return "Standard light (B)";
    825             case 19:
    826                 return "Standard light (C)";
    827             case 20:
    828                 return "D55";
    829             case 21:
    830                 return "D65";
    831             case 22:
    832                 return "D75";
    833             case 255:
    834                 return "(Other)";
    835             default:
    836                 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_WHITE_BALANCE) + ")";
    837         }
    838     }
    839 
    840     public String getMeteringModeDescription() throws MetadataException
    841     {
    842         if (!_directory.containsTag(ExifDirectory.TAG_METERING_MODE)) return null;
    843         // '0' means unknown, '1' average, '2' center weighted average, '3' spot
    844         // '4' multi-spot, '5' multi-segment, '6' partial, '255' other
    845         int meteringMode = _directory.getInt(ExifDirectory.TAG_METERING_MODE);
    846         switch (meteringMode) {
    847             case 0:
    848                 return "Unknown";
    849             case 1:
    850                 return "Average";
    851             case 2:
    852                 return "Center weighted average";
    853             case 3:
    854                 return "Spot";
    855             case 4:
    856                 return "Multi-spot";
    857             case 5:
    858                 return "Multi-segment";
    859             case 6:
    860                 return "Partial";
    861             case 255:
    862                 return "(Other)";
    863             default:
    864                 return "";
    865         }
    866     }
    867 
    868     public String getSubjectDistanceDescription() throws MetadataException
    869     {
    870         if (!_directory.containsTag(ExifDirectory.TAG_SUBJECT_DISTANCE)) return null;
    871         Rational distance = _directory.getRational(ExifDirectory.TAG_SUBJECT_DISTANCE);
    872         java.text.DecimalFormat formatter = new DecimalFormat("0.0##");
    873         return formatter.format(distance.doubleValue()) + " metres";
    874     }
    875 
    876     public String getCompressionLevelDescription() throws MetadataException
    877     {
    878         if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION_LEVEL)) return null;
    879         Rational compressionRatio = _directory.getRational(ExifDirectory.TAG_COMPRESSION_LEVEL);
    880         String ratio = compressionRatio.toSimpleString(_allowDecimalRepresentationOfRationals);
    881         if (compressionRatio.isInteger() && compressionRatio.intValue() == 1) {
    882             return ratio + " bit/pixel";
    883         } else {
    884             return ratio + " bits/pixel";
    885         }
    886     }
    887 
    888     public String getThumbnailLengthDescription()
    889     {
    890         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH)) return null;
    891         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_LENGTH) + " bytes";
    892     }
    893 
    894     public String getThumbnailOffsetDescription()
    895     {
    896         if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET)) return null;
    897         return _directory.getString(ExifDirectory.TAG_THUMBNAIL_OFFSET) + " bytes";
    898     }
    899 
    900     public String getYResolutionDescription() throws MetadataException
    901     {
    902         if (!_directory.containsTag(ExifDirectory.TAG_Y_RESOLUTION)) return null;
    903         Rational resolution = _directory.getRational(ExifDirectory.TAG_Y_RESOLUTION);
    904         return resolution.toSimpleString(_allowDecimalRepresentationOfRationals) +
    905                 " dots per " +
    906                 getResolutionDescription().toLowerCase();
    907     }
    908 
    909     public String getXResolutionDescription() throws MetadataException
    910     {
    911         if (!_directory.containsTag(ExifDirectory.TAG_X_RESOLUTION)) return null;
    912         Rational resolution = _directory.getRational(ExifDirectory.TAG_X_RESOLUTION);
    913         return resolution.toSimpleString(_allowDecimalRepresentationOfRationals) +
    914                 " dots per " +
    915                 getResolutionDescription().toLowerCase();
    916     }
    917 
    918     public String getExposureTimeDescription()
    919     {
    920         if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_TIME)) return null;
    921         return _directory.getString(ExifDirectory.TAG_EXPOSURE_TIME) + " sec";
    922     }
    923 
    924     public String getShutterSpeedDescription() throws MetadataException
    925     {
    926         // I believe this method to now be stable, but am leaving some alternative snippets of
    927         // code in here, to assist anyone who's looking into this (given that I don't have a public CVS).
    928 
    929         if (!_directory.containsTag(ExifDirectory.TAG_SHUTTER_SPEED)) return null;
    930 //        float apexValue = _directory.getFloat(ExifDirectory.TAG_SHUTTER_SPEED);
    931 //        int apexPower = (int)Math.pow(2.0, apexValue);
    932 //        return "1/" + apexPower + " sec";
    933         // TODO test this method
    934         // thanks to Mark Edwards for spotting and patching a bug in the calculation of this
    935         // description (spotted bug using a Canon EOS 300D)
    936         // thanks also to Gli Blr for spotting this bug
    937         float apexValue = _directory.getFloat(ExifDirectory.TAG_SHUTTER_SPEED);
    938         if (apexValue<=1) {
    939             float apexPower = (float)(1/(Math.exp(apexValue*Math.log(2))));
    940             long apexPower10 = Math.round((double)apexPower * 10.0);
    941             float fApexPower = (float) apexPower10 / 10.0f;
    942             return fApexPower + " sec";
    943         } else {
    944             int apexPower = (int)((Math.exp(apexValue*Math.log(2))));
    945             return "1/" + apexPower + " sec";
    946         }
    947 
    948 /*
    949         // This alternative implementation offered by Bill Richards
    950         // TODO determine which is the correct / more-correct implementation
    951         double apexValue = _directory.getDouble(ExifDirectory.TAG_SHUTTER_SPEED);
    952         double apexPower = Math.pow(2.0, apexValue);
    953 
    954         StringBuffer sb = new StringBuffer();
    955         if (apexPower > 1)
    956             apexPower = Math.floor(apexPower);
    957 
    958         if (apexPower < 1) {
    959             sb.append((int)Math.round(1/apexPower));
    960         } else {
    961             sb.append("1/");
    962             sb.append((int)apexPower);
    963         }
    964         sb.append(" sec");
    965         return sb.toString();
    966 */
    967 
    968     }
    969 
    970     public String getFNumberDescription() throws MetadataException
    971     {
    972         if (!_directory.containsTag(ExifDirectory.TAG_FNUMBER)) return null;
    973         Rational fNumber = _directory.getRational(ExifDirectory.TAG_FNUMBER);
    974         return "F" + SimpleDecimalFormatter.format(fNumber.doubleValue());
    975     }
    976 
    977     public String getYCbCrPositioningDescription() throws MetadataException
    978     {
    979         if (!_directory.containsTag(ExifDirectory.TAG_YCBCR_POSITIONING)) return null;
    980         int yCbCrPosition = _directory.getInt(ExifDirectory.TAG_YCBCR_POSITIONING);
    981         switch (yCbCrPosition) {
    982             case 1: return "Center of pixel array";
    983             case 2: return "Datum point";
    984             default:
    985                 return String.valueOf(yCbCrPosition);
    986         }
    987     }
    988 
    989     public String getOrientationDescription() throws MetadataException
    990     {
    991         if (!_directory.containsTag(ExifDirectory.TAG_ORIENTATION)) return null;
    992         int orientation = _directory.getInt(ExifDirectory.TAG_ORIENTATION);
    993         switch (orientation) {
    994             case 1: return "Top, left side (Horizontal / normal)";
    995             case 2: return "Top, right side (Mirror horizontal)";
    996             case 3: return "Bottom, right side (Rotate 180)";
    997             case 4: return "Bottom, left side (Mirror vertical)";
    998             case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)";
    999             case 6: return "Right side, top (Rotate 90 CW)";
    1000             case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)";
    1001             case 8: return "Left side, bottom (Rotate 270 CW)";
    1002             default:
    1003                 return String.valueOf(orientation);
    1004         }
    1005     }
    1006 
    1007     public String getResolutionDescription() throws MetadataException
    1008     {
    1009         if (!_directory.containsTag(ExifDirectory.TAG_RESOLUTION_UNIT)) return "";
    1010         // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
    1011         int resolutionUnit = _directory.getInt(ExifDirectory.TAG_RESOLUTION_UNIT);
    1012         switch (resolutionUnit) {
    1013             case 1: return "(No unit)";
    1014             case 2: return "Inch";
    1015             case 3: return "cm";
    1016             default:
    1017                 return "";
    1018         }
    1019     }
    1020 
    1021     public String getSensingMethodDescription() throws MetadataException
    1022     {
    1023         if (!_directory.containsTag(ExifDirectory.TAG_SENSING_METHOD)) return null;
    1024         // '1' Not defined, '2' One-chip color area sensor, '3' Two-chip color area sensor
    1025         // '4' Three-chip color area sensor, '5' Color sequential area sensor
    1026         // '7' Trilinear sensor '8' Color sequential linear sensor,  'Other' reserved
    1027         int sensingMethod = _directory.getInt(ExifDirectory.TAG_SENSING_METHOD);
    1028         switch (sensingMethod) {
    1029             case 1:
    1030                 return "(Not defined)";
    1031             case 2:
    1032                 return "One-chip color area sensor";
    1033             case 3:
    1034                 return "Two-chip color area sensor";
    1035             case 4:
    1036                 return "Three-chip color area sensor";
    1037             case 5:
    1038                 return "Color sequential area sensor";
    1039             case 7:
    1040                 return "Trilinear sensor";
    1041             case 8:
    1042                 return "Color sequential linear sensor";
    1043             default:
    1044                 return "";
    1045         }
    1046     }
    1047 
    1048     public String getComponentConfigurationDescription() throws MetadataException
    1049     {
    1050         int[] components = _directory.getIntArray(ExifDirectory.TAG_COMPONENTS_CONFIGURATION);
    1051         String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"};
    1052         StringBuffer componentConfig = new StringBuffer();
    1053         for (int i = 0; i < Math.min(4, components.length); i++) {
    1054             int j = components[i];
    1055             if (j > 0 && j < componentStrings.length) {
    1056                 componentConfig.append(componentStrings[j]);
    1057             }
    1058         }
    1059         return componentConfig.toString();
    1060     }
    1061 
    1062     /**
    1063      * Takes a series of 4 bytes from the specified offset, and converts these to a
    1064      * well-known version number, where possible.  For example, (hex) 30 32 31 30 == 2.10).
    1065      * @param components the four version values
    1066      * @return the version as a string of form 2.10
    1067      */
    1068     public static String convertBytesToVersionString(int[] components)
    1069     {
    1070         StringBuffer version = new StringBuffer();
    1071         for (int i = 0; i < 4 && i < components.length; i++) {
    1072             if (i == 2) version.append('.');
    1073             String digit = String.valueOf((char)components[i]);
    1074             if (i == 0 && "0".equals(digit)) continue;
    1075             version.append(digit);
    1076         }
    1077         return version.toString();
    1078     }
    1079 
    1080     /**
    1081      * The Windows specific tags uses plain Unicode
    1082      */
    1083     private String getUnicodeDescription(int tag) throws MetadataException
    1084     {
    1085          if (!_directory.containsTag(tag)) return null;
    1086          byte[] commentBytes = _directory.getByteArray(tag);
    1087          try {
    1088              // decode the unicode string
    1089              // trim it, as i'm seeing a junk character on the end
    1090             return new String(commentBytes, "UTF-16LE").trim();
    1091          }
    1092          catch (UnsupportedEncodingException ex) {
    1093             return null;
    1094          }
    1095     }
    1096 
    1097     public String getWindowsAuthorDescription() throws MetadataException
    1098     {
    1099        return getUnicodeDescription(ExifDirectory.TAG_WIN_AUTHOR);
    1100     }
    1101 
    1102     public String getWindowsCommentDescription() throws MetadataException
    1103     {
    1104        return getUnicodeDescription(ExifDirectory.TAG_WIN_COMMENT);
    1105     }
    1106 
    1107     public String getWindowsKeywordsDescription() throws MetadataException
    1108     {
    1109        return getUnicodeDescription(ExifDirectory.TAG_WIN_KEYWORDS);
    1110     }
    1111 
    1112     public String getWindowsTitleDescription() throws MetadataException
    1113     {
    1114        return getUnicodeDescription(ExifDirectory.TAG_WIN_TITLE);
    1115     }
    1116 
    1117     public String getWindowsSubjectDescription() throws MetadataException
    1118     {
    1119        return getUnicodeDescription(ExifDirectory.TAG_WIN_SUBJECT);
    1120     }
    1121 }
  • src/com/drew/metadata/exif/ExifDirectory.java

     
    1 /*
    2  * ExifDirectory.java
    3  *
    4  * This 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.
    8  *
    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.
    11  *
    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/
    16  *
    17  * Created by dnoakes on 25-Nov-2002 20:41:00 using IntelliJ IDEA.
    18  */
    19 package com.drew.metadata.exif;
    20 
    21 import com.drew.metadata.Directory;
    22 import com.drew.metadata.MetadataException;
    23 
    24 import java.io.FileOutputStream;
    25 import java.io.IOException;
    26 import java.util.HashMap;
    27 
    28 /**
    29  *
    30  */
    31 public class ExifDirectory extends Directory
    32 {
    33     // TODO do these tags belong in the exif directory?
    34     public static final int TAG_SUB_IFDS = 0x014A;
    35     public static final int TAG_GPS_INFO = 0x8825;
    36 
    37     /**
    38      * The actual aperture value of lens when the image was taken. Unit is APEX.
    39      * To convert this value to ordinary F-number (F-stop), calculate this value's
    40      * power of root 2 (=1.4142). For example, if the ApertureValue is '5',
    41      * F-number is 1.4142^5 = F5.6.
    42      */
    43     public static final int TAG_APERTURE = 0x9202;
    44     /**
    45      * When image format is no compression, this value shows the number of bits
    46      * per component for each pixel. Usually this value is '8,8,8'.
    47      */
    48     public static final int TAG_BITS_PER_SAMPLE = 0x0102;
    49     /**
    50      * Shows compression method for Thumbnail.
    51      * 1 = Uncompressed
    52      * 2 = CCITT 1D
    53      * 3 = T4/Group 3 Fax
    54      * 4 = T6/Group 4 Fax
    55      * 5 = LZW
    56      * 6 = JPEG (old-style)
    57      * 7 = JPEG
    58      * 8 = Adobe Deflate
    59      * 9 = JBIG B&W
    60      * 10 = JBIG Color
    61      * 32766 = Next
    62      * 32771 = CCIRLEW
    63      * 32773 = PackBits
    64      * 32809 = Thunderscan
    65      * 32895 = IT8CTPAD
    66      * 32896 = IT8LW
    67      * 32897 = IT8MP
    68      * 32898 = IT8BL
    69      * 32908 = PixarFilm
    70      * 32909 = PixarLog
    71      * 32946 = Deflate
    72      * 32947 = DCS
    73      * 34661 = JBIG
    74      * 34676 = SGILog
    75      * 34677 = SGILog24
    76      * 34712 = JPEG 2000
    77      * 34713 = Nikon NEF Compressed
    78      */
    79     public static final int TAG_COMPRESSION = 0x0103;
    80     public static final int COMPRESSION_NONE = 1;
    81     public static final int COMPRESSION_JPEG = 6;
    82 
    83     /**
    84      * Shows the color space of the image data components.
    85      * 0 = WhiteIsZero
    86      * 1 = BlackIsZero
    87      * 2 = RGB
    88      * 3 = RGB Palette
    89      * 4 = Transparency Mask
    90      * 5 = CMYK
    91      * 6 = YCbCr
    92      * 8 = CIELab
    93      * 9 = ICCLab
    94      * 10 = ITULab
    95      * 32803 = Color Filter Array
    96      * 32844 = Pixar LogL
    97      * 32845 = Pixar LogLuv
    98      * 34892 = Linear Raw
    99      */
    100     public static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106;
    101     /**
    102      * 1 = No dithering or halftoning
    103      * 2 = Ordered dither or halftone
    104      * 3 = Randomized dither
    105      */
    106     public static final int TAG_THRESHOLDING = 0x0107;
    107     public static final int PHOTOMETRIC_INTERPRETATION_MONOCHROME = 1;
    108     public static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
    109     public static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6;
    110 
    111     /** The position in the file of raster data. */
    112     public static final int TAG_STRIP_OFFSETS = 0x0111;
    113     /** Each pixel is composed of this many samples. */
    114     public static final int TAG_SAMPLES_PER_PIXEL = 0x0115;
    115     /** The raster is codified by a single block of data holding this many rows. */
    116     public static final int TAG_ROWS_PER_STRIP = 0x116;
    117     /** The size of the raster data in bytes. */
    118     public static final int TAG_STRIP_BYTE_COUNTS = 0x0117;
    119     public static final int TAG_MIN_SAMPLE_VALUE = 0x0118;
    120     public static final int TAG_MAX_SAMPLE_VALUE = 0x0119;
    121     /**
    122      * When image format is no compression YCbCr, this value shows byte aligns of
    123      * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for
    124      * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and
    125      * stored to Y plane/Cb plane/Cr plane format.
    126      */
    127     public static final int TAG_PLANAR_CONFIGURATION = 0x011C;
    128     public static final int TAG_YCBCR_SUBSAMPLING = 0x0212;
    129     public static final int TAG_IMAGE_DESCRIPTION = 0x010E;
    130     public static final int TAG_SOFTWARE = 0x0131;
    131     public static final int TAG_DATETIME = 0x0132;
    132     public static final int TAG_WHITE_POINT = 0x013E;
    133     public static final int TAG_PRIMARY_CHROMATICITIES = 0x013F;
    134     public static final int TAG_YCBCR_COEFFICIENTS = 0x0211;
    135     public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214;
    136     public static final int TAG_COPYRIGHT = 0x8298;
    137 
    138     /**
    139      * The new subfile type tag.
    140      * 0 = Full-resolution Image
    141      * 1 = Reduced-resolution image
    142      * 2 = Single page of multi-page image
    143      * 3 = Single page of multi-page reduced-resolution image
    144      * 4 = Transparency mask
    145      * 5 = Transparency mask of reduced-resolution image
    146      * 6 = Transparency mask of multi-page image
    147      * 7 = Transparency mask of reduced-resolution multi-page image
    148      */
    149     public static final int TAG_NEW_SUBFILE_TYPE = 0x00FE;
    150     /**
    151      * The old subfile type tag.
    152      * 1 = Full-resolution image (Main image)
    153      * 2 = Reduced-resolution image (Thumbnail)
    154      * 3 = Single page of multi-page image
    155      */
    156     public static final int TAG_SUBFILE_TYPE = 0x00FF;
    157     public static final int TAG_TRANSFER_FUNCTION = 0x012D;
    158     public static final int TAG_ARTIST = 0x013B;
    159     public static final int TAG_PREDICTOR = 0x013D;
    160     public static final int TAG_TILE_WIDTH = 0x0142;
    161     public static final int TAG_TILE_LENGTH = 0x0143;
    162     public static final int TAG_TILE_OFFSETS = 0x0144;
    163     public static final int TAG_TILE_BYTE_COUNTS = 0x0145;
    164     public static final int TAG_JPEG_TABLES = 0x015B;
    165     public static final int TAG_CFA_REPEAT_PATTERN_DIM = 0x828D;
    166     /** There are two definitions for CFA pattern, I don't know the difference... */
    167     public static final int TAG_CFA_PATTERN_2 = 0x828E;
    168     public static final int TAG_BATTERY_LEVEL = 0x828F;
    169     public static final int TAG_IPTC_NAA = 0x83BB;
    170     public static final int TAG_INTER_COLOR_PROFILE = 0x8773;
    171     public static final int TAG_SPECTRAL_SENSITIVITY = 0x8824;
    172     public static final int TAG_OECF = 0x8828;
    173     public static final int TAG_INTERLACE = 0x8829;
    174     public static final int TAG_TIME_ZONE_OFFSET = 0x882A;
    175     public static final int TAG_SELF_TIMER_MODE = 0x882B;
    176     public static final int TAG_FLASH_ENERGY = 0x920B;
    177     public static final int TAG_SPATIAL_FREQ_RESPONSE = 0x920C;
    178     public static final int TAG_NOISE = 0x920D;
    179     public static final int TAG_IMAGE_NUMBER = 0x9211;
    180     public static final int TAG_SECURITY_CLASSIFICATION = 0x9212;
    181     public static final int TAG_IMAGE_HISTORY = 0x9213;
    182     public static final int TAG_SUBJECT_LOCATION = 0x9214;
    183     /** There are two definitions for exposure index, I don't know the difference... */
    184     public static final int TAG_EXPOSURE_INDEX_2 = 0x9215;
    185     public static final int TAG_TIFF_EP_STANDARD_ID = 0x9216;
    186     public static final int TAG_FLASH_ENERGY_2 = 0xA20B;
    187     public static final int TAG_SPATIAL_FREQ_RESPONSE_2 = 0xA20C;
    188     public static final int TAG_SUBJECT_LOCATION_2 = 0xA214;
    189     public static final int TAG_MAKE = 0x010F;
    190     public static final int TAG_MODEL = 0x0110;
    191     public static final int TAG_ORIENTATION = 0x0112;
    192     public static final int TAG_X_RESOLUTION = 0x011A;
    193     public static final int TAG_Y_RESOLUTION = 0x011B;
    194     public static final int TAG_PAGE_NAME = 0x011D;
    195     public static final int TAG_RESOLUTION_UNIT = 0x0128;
    196     public static final int TAG_THUMBNAIL_OFFSET = 0x0201;
    197     public static final int TAG_THUMBNAIL_LENGTH = 0x0202;
    198     public static final int TAG_YCBCR_POSITIONING = 0x0213;
    199     /**
    200      * Exposure time (reciprocal of shutter speed). Unit is second.
    201      */
    202     public static final int TAG_EXPOSURE_TIME = 0x829A;
    203     /**
    204      * The actual F-number(F-stop) of lens when the image was taken.
    205      */
    206     public static final int TAG_FNUMBER = 0x829D;
    207     /**
    208      * Exposure program that the camera used when image was taken. '1' means
    209      * manual control, '2' program normal, '3' aperture priority, '4' shutter
    210      * priority, '5' program creative (slow program), '6' program action
    211      * (high-speed program), '7' portrait mode, '8' landscape mode.
    212      */
    213     public static final int TAG_EXPOSURE_PROGRAM = 0x8822;
    214     public static final int TAG_ISO_EQUIVALENT = 0x8827;
    215     public static final int TAG_EXIF_VERSION = 0x9000;
    216     public static final int TAG_DATETIME_ORIGINAL = 0x9003;
    217     public static final int TAG_DATETIME_DIGITIZED = 0x9004;
    218     public static final int TAG_COMPONENTS_CONFIGURATION = 0x9101;
    219     /**
    220      * Average (rough estimate) compression level in JPEG bits per pixel.
    221      * */
    222     public static final int TAG_COMPRESSION_LEVEL = 0x9102;
    223     /**
    224      * Shutter speed by APEX value. To convert this value to ordinary 'Shutter Speed';
    225      * calculate this value's power of 2, then reciprocal. For example, if the
    226      * ShutterSpeedValue is '4', shutter speed is 1/(24)=1/16 second.
    227      */
    228     public static final int TAG_SHUTTER_SPEED = 0x9201;
    229     public static final int TAG_BRIGHTNESS_VALUE = 0x9203;
    230     public static final int TAG_EXPOSURE_BIAS = 0x9204;
    231     /**
    232      * Maximum aperture value of lens. You can convert to F-number by calculating
    233      * power of root 2 (same process of ApertureValue:0x9202).
    234      * The actual aperture value of lens when the image was taken. To convert this
    235      * value to ordinary f-number(f-stop), calculate the value's power of root 2
    236      * (=1.4142). For example, if the ApertureValue is '5', f-number is 1.41425^5 = F5.6.
    237      */
    238     public static final int TAG_MAX_APERTURE = 0x9205;
    239     /**
    240      * Indicates the distance the autofocus camera is focused to.  Tends to be less accurate as distance increases.
    241      */
    242     public static final int TAG_SUBJECT_DISTANCE = 0x9206;
    243     /**
    244      * Exposure metering method. '0' means unknown, '1' average, '2' center
    245      * weighted average, '3' spot, '4' multi-spot, '5' multi-segment, '6' partial,
    246      * '255' other.
    247      */
    248     public static final int TAG_METERING_MODE = 0x9207;
    249 
    250     public static final int TAG_LIGHT_SOURCE = 0x9208;
    251     /**
    252      * White balance (aka light source). '0' means unknown, '1' daylight,
    253      * '2' fluorescent, '3' tungsten, '10' flash, '17' standard light A,
    254      * '18' standard light B, '19' standard light C, '20' D55, '21' D65,
    255      * '22' D75, '255' other.
    256      */
    257     public static final int TAG_WHITE_BALANCE = 0x9208;
    258     /**
    259      * 0x0  = 0000000 = No Flash
    260      * 0x1  = 0000001 = Fired
    261      * 0x5  = 0000101 = Fired, Return not detected
    262      * 0x7  = 0000111 = Fired, Return detected
    263      * 0x9  = 0001001 = On
    264      * 0xd  = 0001101 = On, Return not detected
    265      * 0xf  = 0001111 = On, Return detected
    266      * 0x10 = 0010000 = Off
    267      * 0x18 = 0011000 = Auto, Did not fire
    268      * 0x19 = 0011001 = Auto, Fired
    269      * 0x1d = 0011101 = Auto, Fired, Return not detected
    270      * 0x1f = 0011111 = Auto, Fired, Return detected
    271      * 0x20 = 0100000 = No flash function
    272      * 0x41 = 1000001 = Fired, Red-eye reduction
    273      * 0x45 = 1000101 = Fired, Red-eye reduction, Return not detected
    274      * 0x47 = 1000111 = Fired, Red-eye reduction, Return detected
    275      * 0x49 = 1001001 = On, Red-eye reduction
    276      * 0x4d = 1001101 = On, Red-eye reduction, Return not detected
    277      * 0x4f = 1001111 = On, Red-eye reduction, Return detected
    278      * 0x59 = 1011001 = Auto, Fired, Red-eye reduction
    279      * 0x5d = 1011101 = Auto, Fired, Red-eye reduction, Return not detected
    280      * 0x5f = 1011111 = Auto, Fired, Red-eye reduction, Return detected
    281      *        6543210 (positions)
    282      *
    283      * This is a bitmask.
    284      * 0 = flash fired
    285      * 1 = return detected
    286      * 2 = return able to be detected
    287      * 3 = unknown
    288      * 4 = auto used
    289      * 5 = unknown
    290      * 6 = red eye reduction used
    291      */
    292     public static final int TAG_FLASH = 0x9209;
    293     /**
    294      * Focal length of lens used to take image.  Unit is millimeter.
    295      * Nice digital cameras actually save the focal length as a function of how far they are zoomed in.
    296      */
    297     public static final int TAG_FOCAL_LENGTH = 0x920A;
    298     public static final int TAG_USER_COMMENT = 0x9286;
    299     public static final int TAG_SUBSECOND_TIME = 0x9290;
    300     public static final int TAG_SUBSECOND_TIME_ORIGINAL = 0x9291;
    301     public static final int TAG_SUBSECOND_TIME_DIGITIZED = 0x9292;
    302     public static final int TAG_FLASHPIX_VERSION = 0xA000;
    303     /**
    304      * Defines Color Space. DCF image must use sRGB color space so value is
    305      * always '1'. If the picture uses the other color space, value is
    306      * '65535':Uncalibrated.
    307      */
    308     public static final int TAG_COLOR_SPACE = 0xA001;
    309     public static final int TAG_EXIF_IMAGE_WIDTH = 0xA002;
    310     public static final int TAG_EXIF_IMAGE_HEIGHT = 0xA003;
    311     public static final int TAG_RELATED_SOUND_FILE = 0xA004;
    312     public static final int TAG_FOCAL_PLANE_X_RES = 0xA20E;
    313     public static final int TAG_FOCAL_PLANE_Y_RES = 0xA20F;
    314     /**
    315      * Unit of FocalPlaneXResoluton/FocalPlaneYResolution. '1' means no-unit,
    316      * '2' inch, '3' centimeter.
    317      *
    318      * Note: Some of Fujifilm's digicam(e.g.FX2700,FX2900,Finepix4700Z/40i etc)
    319      * uses value '3' so it must be 'centimeter', but it seems that they use a
    320      * '8.3mm?'(1/3in.?) to their ResolutionUnit. Fuji's BUG? Finepix4900Z has
    321      * been changed to use value '2' but it doesn't match to actual value also.
    322      */
    323     public static final int TAG_FOCAL_PLANE_UNIT = 0xA210;
    324     public static final int TAG_EXPOSURE_INDEX = 0xA215;
    325     public static final int TAG_SENSING_METHOD = 0xA217;
    326     public static final int TAG_FILE_SOURCE = 0xA300;
    327     public static final int TAG_SCENE_TYPE = 0xA301;
    328     public static final int TAG_CFA_PATTERN = 0xA302;
    329 
    330     // these tags new with Exif 2.2 (?) [A401 - A4
    331     /**
    332      * This tag indicates the use of special processing on image data, such as rendering
    333      * geared to output. When special processing is performed, the reader is expected to
    334      * disable or minimize any further processing.
    335      * Tag = 41985 (A401.H)
    336      * Type = SHORT
    337      * Count = 1
    338      * Default = 0
    339      *   0 = Normal process
    340      *   1 = Custom process
    341      *   Other = reserved
    342      */
    343     public static final int TAG_CUSTOM_RENDERED = 0xA401;
    344 
    345     /**
    346      * This tag indicates the exposure mode set when the image was shot. In auto-bracketing
    347      * mode, the camera shoots a series of frames of the same scene at different exposure settings.
    348      * Tag = 41986 (A402.H)
    349      * Type = SHORT
    350      * Count = 1
    351      * Default = none
    352      *   0 = Auto exposure
    353      *   1 = Manual exposure
    354      *   2 = Auto bracket
    355      *   Other = reserved
    356      */
    357     public static final int TAG_EXPOSURE_MODE = 0xA402;
    358 
    359     /**
    360      * This tag indicates the white balance mode set when the image was shot.
    361      * Tag = 41987 (A403.H)
    362      * Type = SHORT
    363      * Count = 1
    364      * Default = none
    365      *   0 = Auto white balance
    366      *   1 = Manual white balance
    367      *   Other = reserved
    368      */
    369     public static final int TAG_WHITE_BALANCE_MODE = 0xA403;
    370 
    371     /**
    372      * This tag indicates the digital zoom ratio when the image was shot. If the
    373      * numerator of the recorded value is 0, this indicates that digital zoom was
    374      * not used.
    375      * Tag = 41988 (A404.H)
    376      * Type = RATIONAL
    377      * Count = 1
    378      * Default = none
    379      */
    380     public static final int TAG_DIGITAL_ZOOM_RATIO = 0xA404;
    381 
    382     /**
    383      * This tag indicates the equivalent focal length assuming a 35mm film camera,
    384      * in mm. A value of 0 means the focal length is unknown. Note that this tag
    385      * differs from the FocalLength tag.
    386      * Tag = 41989 (A405.H)
    387      * Type = SHORT
    388      * Count = 1
    389      * Default = none
    390      */
    391     public static final int TAG_35MM_FILM_EQUIV_FOCAL_LENGTH = 0xA405;
    392 
    393     /**
    394      * This tag indicates the type of scene that was shot. It can also be used to
    395      * record the mode in which the image was shot. Note that this differs from
    396      * the scene type (SceneType) tag.
    397      * Tag = 41990 (A406.H)
    398      * Type = SHORT
    399      * Count = 1
    400      * Default = 0
    401      *   0 = Standard
    402      *   1 = Landscape
    403      *   2 = Portrait
    404      *   3 = Night scene
    405      *   Other = reserved
    406      */
    407     public static final int TAG_SCENE_CAPTURE_TYPE = 0xA406;
    408 
    409     /**
    410      * This tag indicates the degree of overall image gain adjustment.
    411      * Tag = 41991 (A407.H)
    412      * Type = SHORT
    413      * Count = 1
    414      * Default = none
    415      *   0 = None
    416      *   1 = Low gain up
    417      *   2 = High gain up
    418      *   3 = Low gain down
    419      *   4 = High gain down
    420      *   Other = reserved
    421      */
    422     public static final int TAG_GAIN_CONTROL = 0xA407;
    423 
    424     /**
    425      * This tag indicates the direction of contrast processing applied by the camera
    426      * when the image was shot.
    427      * Tag = 41992 (A408.H)
    428      * Type = SHORT
    429      * Count = 1
    430      * Default = 0
    431      *   0 = Normal
    432      *   1 = Soft
    433      *   2 = Hard
    434      *   Other = reserved
    435      */
    436     public static final int TAG_CONTRAST = 0xA408;
    437 
    438     /**
    439      * This tag indicates the direction of saturation processing applied by the camera
    440      * when the image was shot.
    441      * Tag = 41993 (A409.H)
    442      * Type = SHORT
    443      * Count = 1
    444      * Default = 0
    445      *   0 = Normal
    446      *   1 = Low saturation
    447      *   2 = High saturation
    448      *   Other = reserved
    449      */
    450     public static final int TAG_SATURATION = 0xA409;
    451 
    452     /**
    453      * This tag indicates the direction of sharpness processing applied by the camera
    454      * when the image was shot.
    455      * Tag = 41994 (A40A.H)
    456      * Type = SHORT
    457      * Count = 1
    458      * Default = 0
    459      *   0 = Normal
    460      *   1 = Soft
    461      *   2 = Hard
    462      *   Other = reserved
    463      */
    464     public static final int TAG_SHARPNESS = 0xA40A;
    465 
    466     // TODO support this tag (I haven't seen a camera's actual implementation of this yet)
    467 
    468     /**
    469      * This tag indicates information on the picture-taking conditions of a particular
    470      * camera model. The tag is used only to indicate the picture-taking conditions in
    471      * the reader.
    472      * Tag = 41995 (A40B.H)
    473      * Type = UNDEFINED
    474      * Count = Any
    475      * Default = none
    476      *
    477      * The information is recorded in the format shown below. The data is recorded
    478      * in Unicode using SHORT type for the number of display rows and columns and
    479      * UNDEFINED type for the camera settings. The Unicode (UCS-2) string including
    480      * Signature is NULL terminated. The specifics of the Unicode string are as given
    481      * in ISO/IEC 10464-1.
    482      *
    483      *      Length  Type        Meaning
    484      *      ------+-----------+------------------
    485      *      2       SHORT       Display columns
    486      *      2       SHORT       Display rows
    487      *      Any     UNDEFINED   Camera setting-1
    488      *      Any     UNDEFINED   Camera setting-2
    489      *      :       :           :
    490      *      Any     UNDEFINED   Camera setting-n
    491      */
    492     public static final int TAG_DEVICE_SETTING_DESCRIPTION = 0xA40B;
    493 
    494     /**
    495      * This tag indicates the distance to the subject.
    496      * Tag = 41996 (A40C.H)
    497      * Type = SHORT
    498      * Count = 1
    499      * Default = none
    500      *   0 = unknown
    501      *   1 = Macro
    502      *   2 = Close view
    503      *   3 = Distant view
    504      *   Other = reserved
    505      */
    506     public static final int TAG_SUBJECT_DISTANCE_RANGE = 0xA40C;
    507 
    508     /**
    509      * The image title, as used by Windows XP.
    510      */
    511     public static final int TAG_WIN_TITLE = 0x9C9B;
    512 
    513     /**
    514      * The image comment, as used by Windows XP.
    515      */
    516     public static final int TAG_WIN_COMMENT = 0x9C9C;
    517 
    518     /**
    519      * The image author, as used by Windows XP (called Artist in the Windows shell).
    520      */
    521     public static final int TAG_WIN_AUTHOR = 0x9C9D;
    522 
    523     /**
    524      * The image keywords, as used by Windows XP.
    525      */
    526     public static final int TAG_WIN_KEYWORDS = 0x9C9E;
    527 
    528     /**
    529      * The image subject, as used by Windows XP.
    530      */
    531     public static final int TAG_WIN_SUBJECT = 0x9C9F;
    532 
    533     /**
    534      * This tag indicates an identifier assigned uniquely to each image. It is
    535      * recorded as an ASCII string equivalent to hexadecimal notation and 128-bit
    536      * fixed length.
    537      * Tag = 42016 (A420.H)
    538      * Type = ASCII
    539      * Count = 33
    540      * Default = none
    541      */
    542     public static final int TAG_IMAGE_UNIQUE_ID = 0xA420;
    543 
    544     public static final int TAG_THUMBNAIL_IMAGE_WIDTH = 0x0100;
    545     public static final int TAG_THUMBNAIL_IMAGE_HEIGHT = 0x0101;
    546     public static final int TAG_THUMBNAIL_DATA = 0xF001;
    547 
    548     /**
    549      * 1 = Normal
    550      * 2 = Reversed
    551      */
    552     public static final int TAG_FILL_ORDER = 0x010A;
    553     public static final int TAG_DOCUMENT_NAME = 0x010D;
    554 
    555     protected static final HashMap tagNameMap = new HashMap();
    556 
    557     static
    558     {
    559         tagNameMap.put(new Integer(TAG_FILL_ORDER), "Fill Order");
    560         tagNameMap.put(new Integer(TAG_DOCUMENT_NAME), "Document Name");
    561         tagNameMap.put(new Integer(0x1000), "Related Image File Format");
    562         tagNameMap.put(new Integer(0x1001), "Related Image Width");
    563         tagNameMap.put(new Integer(0x1002), "Related Image Length");
    564         tagNameMap.put(new Integer(0x0156), "Transfer Range");
    565         tagNameMap.put(new Integer(0x0200), "JPEG Proc");
    566         tagNameMap.put(new Integer(0x8769), "Exif Offset");
    567         tagNameMap.put(new Integer(TAG_COMPRESSION_LEVEL), "Compressed Bits Per Pixel");
    568         tagNameMap.put(new Integer(0x927C), "Maker Note");
    569         tagNameMap.put(new Integer(0xA005), "Interoperability Offset");
    570 
    571         tagNameMap.put(new Integer(TAG_NEW_SUBFILE_TYPE), "New Subfile Type");
    572         tagNameMap.put(new Integer(TAG_SUBFILE_TYPE), "Subfile Type");
    573         tagNameMap.put(new Integer(TAG_THUMBNAIL_IMAGE_WIDTH), "Thumbnail Image Width");
    574         tagNameMap.put(new Integer(TAG_THUMBNAIL_IMAGE_HEIGHT), "Thumbnail Image Height");
    575         tagNameMap.put(new Integer(TAG_BITS_PER_SAMPLE), "Bits Per Sample");
    576         tagNameMap.put(new Integer(TAG_COMPRESSION), "Compression");
    577         tagNameMap.put(new Integer(TAG_PHOTOMETRIC_INTERPRETATION), "Photometric Interpretation");
    578         tagNameMap.put(new Integer(TAG_THRESHOLDING), "Thresholding");
    579         tagNameMap.put(new Integer(TAG_IMAGE_DESCRIPTION), "Image Description");
    580         tagNameMap.put(new Integer(TAG_MAKE), "Make");
    581         tagNameMap.put(new Integer(TAG_MODEL), "Model");
    582         tagNameMap.put(new Integer(TAG_STRIP_OFFSETS), "Strip Offsets");
    583         tagNameMap.put(new Integer(TAG_ORIENTATION), "Orientation");
    584         tagNameMap.put(new Integer(TAG_SAMPLES_PER_PIXEL), "Samples Per Pixel");
    585         tagNameMap.put(new Integer(TAG_ROWS_PER_STRIP), "Rows Per Strip");
    586         tagNameMap.put(new Integer(TAG_STRIP_BYTE_COUNTS), "Strip Byte Counts");
    587         tagNameMap.put(new Integer(TAG_X_RESOLUTION), "X Resolution");
    588         tagNameMap.put(new Integer(TAG_Y_RESOLUTION), "Y Resolution");
    589         tagNameMap.put(new Integer(TAG_PAGE_NAME), "Page Name");
    590         tagNameMap.put(new Integer(TAG_PLANAR_CONFIGURATION), "Planar Configuration");
    591         tagNameMap.put(new Integer(TAG_RESOLUTION_UNIT), "Resolution Unit");
    592         tagNameMap.put(new Integer(TAG_TRANSFER_FUNCTION), "Transfer Function");
    593         tagNameMap.put(new Integer(TAG_SOFTWARE), "Software");
    594         tagNameMap.put(new Integer(TAG_DATETIME), "Date/Time");
    595         tagNameMap.put(new Integer(TAG_ARTIST), "Artist");
    596         tagNameMap.put(new Integer(TAG_PREDICTOR), "Predictor");
    597         tagNameMap.put(new Integer(TAG_WHITE_POINT), "White Point");
    598         tagNameMap.put(new Integer(TAG_PRIMARY_CHROMATICITIES), "Primary Chromaticities");
    599         tagNameMap.put(new Integer(TAG_TILE_WIDTH), "Tile Width");
    600         tagNameMap.put(new Integer(TAG_TILE_LENGTH), "Tile Length");
    601         tagNameMap.put(new Integer(TAG_TILE_OFFSETS), "Tile Offsets");
    602         tagNameMap.put(new Integer(TAG_TILE_BYTE_COUNTS), "Tile Byte Counts");
    603         tagNameMap.put(new Integer(TAG_SUB_IFDS), "Sub IFDs");
    604         tagNameMap.put(new Integer(TAG_JPEG_TABLES), "JPEG Tables");
    605         tagNameMap.put(new Integer(TAG_THUMBNAIL_OFFSET), "Thumbnail Offset");
    606         tagNameMap.put(new Integer(TAG_THUMBNAIL_LENGTH), "Thumbnail Length");
    607         tagNameMap.put(new Integer(TAG_THUMBNAIL_DATA), "Thumbnail Data");
    608         tagNameMap.put(new Integer(TAG_YCBCR_COEFFICIENTS), "YCbCr Coefficients");
    609         tagNameMap.put(new Integer(TAG_YCBCR_SUBSAMPLING), "YCbCr Sub-Sampling");
    610         tagNameMap.put(new Integer(TAG_YCBCR_POSITIONING), "YCbCr Positioning");
    611         tagNameMap.put(new Integer(TAG_REFERENCE_BLACK_WHITE), "Reference Black/White");
    612         tagNameMap.put(new Integer(TAG_CFA_REPEAT_PATTERN_DIM), "CFA Repeat Pattern Dim");
    613         tagNameMap.put(new Integer(TAG_CFA_PATTERN_2), "CFA Pattern");
    614         tagNameMap.put(new Integer(TAG_BATTERY_LEVEL), "Battery Level");
    615         tagNameMap.put(new Integer(TAG_COPYRIGHT), "Copyright");
    616         tagNameMap.put(new Integer(TAG_EXPOSURE_TIME), "Exposure Time");
    617         tagNameMap.put(new Integer(TAG_FNUMBER), "F-Number");
    618         tagNameMap.put(new Integer(TAG_IPTC_NAA), "IPTC/NAA");
    619         tagNameMap.put(new Integer(TAG_INTER_COLOR_PROFILE), "Inter Color Profile");
    620         tagNameMap.put(new Integer(TAG_EXPOSURE_PROGRAM), "Exposure Program");
    621         tagNameMap.put(new Integer(TAG_SPECTRAL_SENSITIVITY), "Spectral Sensitivity");
    622         tagNameMap.put(new Integer(TAG_GPS_INFO), "GPS Info");
    623         tagNameMap.put(new Integer(TAG_ISO_EQUIVALENT), "ISO Speed Ratings");
    624         tagNameMap.put(new Integer(TAG_OECF), "OECF");
    625         tagNameMap.put(new Integer(TAG_INTERLACE), "Interlace");
    626         tagNameMap.put(new Integer(TAG_TIME_ZONE_OFFSET), "Time Zone Offset");
    627         tagNameMap.put(new Integer(TAG_SELF_TIMER_MODE), "Self Timer Mode");
    628         tagNameMap.put(new Integer(TAG_EXIF_VERSION), "Exif Version");
    629         tagNameMap.put(new Integer(TAG_DATETIME_ORIGINAL), "Date/Time Original");
    630         tagNameMap.put(new Integer(TAG_DATETIME_DIGITIZED), "Date/Time Digitized");
    631         tagNameMap.put(new Integer(TAG_COMPONENTS_CONFIGURATION), "Components Configuration");
    632         tagNameMap.put(new Integer(TAG_SHUTTER_SPEED), "Shutter Speed Value");
    633         tagNameMap.put(new Integer(TAG_APERTURE), "Aperture Value");
    634         tagNameMap.put(new Integer(TAG_BRIGHTNESS_VALUE), "Brightness Value");
    635         tagNameMap.put(new Integer(TAG_EXPOSURE_BIAS), "Exposure Bias Value");
    636         tagNameMap.put(new Integer(TAG_MAX_APERTURE), "Max Aperture Value");
    637         tagNameMap.put(new Integer(TAG_SUBJECT_DISTANCE), "Subject Distance");
    638         tagNameMap.put(new Integer(TAG_METERING_MODE), "Metering Mode");
    639         tagNameMap.put(new Integer(TAG_WHITE_BALANCE), "Light Source");
    640         tagNameMap.put(new Integer(TAG_FLASH), "Flash");
    641         tagNameMap.put(new Integer(TAG_FOCAL_LENGTH), "Focal Length");
    642         tagNameMap.put(new Integer(TAG_FLASH_ENERGY), "Flash Energy");
    643         tagNameMap.put(new Integer(TAG_SPATIAL_FREQ_RESPONSE), "Spatial Frequency Response");
    644         tagNameMap.put(new Integer(TAG_NOISE), "Noise");
    645         tagNameMap.put(new Integer(TAG_IMAGE_NUMBER), "Image Number");
    646         tagNameMap.put(new Integer(TAG_SECURITY_CLASSIFICATION), "Security Classification");
    647         tagNameMap.put(new Integer(TAG_IMAGE_HISTORY), "Image History");
    648         tagNameMap.put(new Integer(TAG_SUBJECT_LOCATION), "Subject Location");
    649         tagNameMap.put(new Integer(TAG_EXPOSURE_INDEX), "Exposure Index");
    650         tagNameMap.put(new Integer(TAG_TIFF_EP_STANDARD_ID), "TIFF/EP Standard ID");
    651         tagNameMap.put(new Integer(TAG_USER_COMMENT), "User Comment");
    652         tagNameMap.put(new Integer(TAG_SUBSECOND_TIME), "Sub-Sec Time");
    653         tagNameMap.put(new Integer(TAG_SUBSECOND_TIME_ORIGINAL), "Sub-Sec Time Original");
    654         tagNameMap.put(new Integer(TAG_SUBSECOND_TIME_DIGITIZED), "Sub-Sec Time Digitized");
    655         tagNameMap.put(new Integer(TAG_FLASHPIX_VERSION), "FlashPix Version");
    656         tagNameMap.put(new Integer(TAG_COLOR_SPACE), "Color Space");
    657         tagNameMap.put(new Integer(TAG_EXIF_IMAGE_WIDTH), "Exif Image Width");
    658         tagNameMap.put(new Integer(TAG_EXIF_IMAGE_HEIGHT), "Exif Image Height");
    659         tagNameMap.put(new Integer(TAG_RELATED_SOUND_FILE), "Related Sound File");
    660         // 0x920B in TIFF/EP
    661         tagNameMap.put(new Integer(TAG_FLASH_ENERGY_2), "Flash Energy");
    662         // 0x920C in TIFF/EP
    663         tagNameMap.put(new Integer(TAG_SPATIAL_FREQ_RESPONSE_2), "Spatial Frequency Response");
    664         // 0x920E in TIFF/EP
    665         tagNameMap.put(new Integer(TAG_FOCAL_PLANE_X_RES), "Focal Plane X Resolution");
    666         // 0x920F in TIFF/EP
    667         tagNameMap.put(new Integer(TAG_FOCAL_PLANE_Y_RES), "Focal Plane Y Resolution");
    668         // 0x9210 in TIFF/EP
    669         tagNameMap.put(new Integer(TAG_FOCAL_PLANE_UNIT), "Focal Plane Resolution Unit");
    670         // 0x9214 in TIFF/EP
    671         tagNameMap.put(new Integer(TAG_SUBJECT_LOCATION_2), "Subject Location");
    672         // 0x9215 in TIFF/EP
    673         tagNameMap.put(new Integer(TAG_EXPOSURE_INDEX_2), "Exposure Index");
    674         // 0x9217 in TIFF/EP
    675         tagNameMap.put(new Integer(TAG_SENSING_METHOD), "Sensing Method");
    676         tagNameMap.put(new Integer(TAG_FILE_SOURCE), "File Source");
    677         tagNameMap.put(new Integer(TAG_SCENE_TYPE), "Scene Type");
    678         tagNameMap.put(new Integer(TAG_CFA_PATTERN), "CFA Pattern");
    679 
    680         tagNameMap.put(new Integer(TAG_CUSTOM_RENDERED), "Custom Rendered");
    681         tagNameMap.put(new Integer(TAG_EXPOSURE_MODE), "Exposure Mode");
    682         tagNameMap.put(new Integer(TAG_WHITE_BALANCE_MODE), "White Balance");
    683         tagNameMap.put(new Integer(TAG_DIGITAL_ZOOM_RATIO), "Digital Zoom Ratio");
    684         tagNameMap.put(new Integer(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH), "Focal Length 35");
    685         tagNameMap.put(new Integer(TAG_SCENE_CAPTURE_TYPE), "Scene Capture Type");
    686         tagNameMap.put(new Integer(TAG_GAIN_CONTROL), "Gain Control");
    687         tagNameMap.put(new Integer(TAG_CONTRAST), "Contrast");
    688         tagNameMap.put(new Integer(TAG_SATURATION), "Saturation");
    689         tagNameMap.put(new Integer(TAG_SHARPNESS), "Sharpness");
    690         tagNameMap.put(new Integer(TAG_DEVICE_SETTING_DESCRIPTION), "Device Setting Description");
    691         tagNameMap.put(new Integer(TAG_SUBJECT_DISTANCE_RANGE), "Subject Distance Range");
    692 
    693         tagNameMap.put(new Integer(TAG_WIN_AUTHOR), "Windows XP Author");
    694         tagNameMap.put(new Integer(TAG_WIN_COMMENT), "Windows XP Comment");
    695         tagNameMap.put(new Integer(TAG_WIN_KEYWORDS), "Windows XP Keywords");
    696         tagNameMap.put(new Integer(TAG_WIN_SUBJECT), "Windows XP Subject");
    697         tagNameMap.put(new Integer(TAG_WIN_TITLE), "Windows XP Title");
    698 
    699         tagNameMap.put(new Integer(TAG_MIN_SAMPLE_VALUE), "Minimum sample value");
    700         tagNameMap.put(new Integer(TAG_MAX_SAMPLE_VALUE), "Maximum sample value");
    701     }
    702 
    703     public ExifDirectory()
    704     {
    705         this.setDescriptor(new ExifDescriptor(this));
    706     }
    707 
    708     public String getName()
    709     {
    710         return "Exif";
    711     }
    712 
    713     protected HashMap getTagNameMap()
    714     {
    715         return tagNameMap;
    716     }
    717 
    718     public byte[] getThumbnailData() throws MetadataException
    719     {
    720         if (!containsThumbnail())
    721             return null;
    722        
    723         return this.getByteArray(ExifDirectory.TAG_THUMBNAIL_DATA);
    724     }
    725 
    726     public void writeThumbnail(String filename) throws MetadataException, IOException
    727     {
    728         byte[] data = getThumbnailData();
    729 
    730         if (data==null)
    731             throw new MetadataException("No thumbnail data exists.");
    732 
    733         FileOutputStream stream = null;
    734         try {
    735             stream = new FileOutputStream(filename);
    736             stream.write(data);
    737         } finally {
    738             if (stream!=null)
    739                 stream.close();
    740         }
    741     }
    742 
    743 /*
    744     // This thumbnail extraction code is not complete, and is included to assist anyone who feels like looking into
    745     // it.  Please share any progress with the original author, and hence the community.  Thanks.
    746 
    747     /**
    748      *
    749      * @return
    750      * @throws MetadataException
    751      * /
    752     public Image getThumbnailImage() throws MetadataException
    753     {
    754         if (!containsThumbnail())
    755             return null;
    756 
    757         int compression = 0;
    758         try {
    759                 compression = this.getInt(ExifDirectory.TAG_COMPRESSION);
    760         } catch (Throwable e) {
    761                 this.addError("Unable to determine thumbnail type " + e.getMessage());
    762         }
    763 
    764         final byte[] thumbnailBytes = getThumbnailData();
    765 
    766         if (compression == ExifDirectory.COMPRESSION_JPEG)
    767         {
    768             // JPEG Thumbnail
    769             // operate directly on thumbnailBytes
    770 //            try {
    771 //                int offset = this.getInt(ExifDirectory.TAG_THUMBNAIL_OFFSET);
    772 //                int length = this.getInt(ExifDirectory.TAG_THUMBNAIL_LENGTH);
    773 //                byte[] result = new byte[length];
    774 //                for (int i = 0; i<result.length; i++) {
    775 //                    result[i] = _data[tiffHeaderOffset + offset + i];
    776 //                }
    777 //                this.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);
    778 //            } catch (Throwable e) {
    779 //                this.addError("Unable to extract thumbnail: " + e.getMessage());
    780 //            }
    781             // TODO decode the JPEG bytes as an image
    782             return null; //new Image();
    783         }
    784         else if (compression == ExifDirectory.COMPRESSION_NONE)
    785         {
    786             // uncompressed thumbnail (raw RGB data)
    787                 if (!this.containsTag(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION))
    788                     return null;
    789 
    790                 try
    791             {
    792                         // If the image is RGB format, then convert it to a bitmap
    793                 final int photometricInterpretation = this.getInt(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION);
    794                 if (photometricInterpretation == ExifDirectory.PHOTOMETRIC_INTERPRETATION_RGB)
    795                 {
    796                     // RGB
    797                     Image image = createImageFromRawRgb(thumbnailBytes);
    798                     return image;
    799                         }
    800                 else if (photometricInterpretation == ExifDirectory.PHOTOMETRIC_INTERPRETATION_YCBCR)
    801                 {
    802                     // YCbCr
    803                     Image image = createImageFromRawYCbCr(thumbnailBytes);
    804                     return image;
    805                         }
    806                 else if (photometricInterpretation == ExifDirectory.PHOTOMETRIC_INTERPRETATION_MONOCHROME)
    807                 {
    808                     // Monochrome
    809                     // TODO
    810                     return null;
    811                 }
    812                 } catch (Throwable e) {
    813                     this.addError("Unable to extract thumbnail: " + e.getMessage());
    814                 }
    815         }
    816         return null;
    817     }
    818 
    819     /**
    820      * Handle the YCbCr thumbnail encoding used by Ricoh RDC4200/4300, Fuji DS-7/300 and DX-5/7/9 cameras.
    821      *
    822      * At DX-5/7/9, YCbCrSubsampling(0x0212) has values of '2,1', PlanarConfiguration(0x011c) has a value '1'. So the
    823      * data align of this image is below.
    824      *
    825      * Y(0,0),Y(1,0),Cb(0,0),Cr(0,0), Y(2,0),Y(3,0),Cb(2,0),Cr(3.0), Y(4,0),Y(5,0),Cb(4,0),Cr(4,0). . . .
    826      *
    827      * The numerics in parenthesis are pixel coordinates. DX series' YCbCrCoefficients(0x0211) has values '0.299/0.587/0.114',
    828      * ReferenceBlackWhite(0x0214) has values '0,255,128,255,128,255'. Therefore to convert from Y/Cb/Cr to RGB is;
    829      *
    830      * B(0,0)=(Cb-128)*(2-0.114*2)+Y(0,0)
    831      * R(0,0)=(Cr-128)*(2-0.299*2)+Y(0,0)
    832      * G(0,0)=(Y(0,0)-0.114*B(0,0)-0.299*R(0,0))/0.587
    833      *
    834      * Horizontal subsampling is a value '2', so you can calculate B(1,0)/R(1,0)/G(1,0) by using the Y(1,0) and Cr(0,0)/Cb(0,0).
    835      * Repeat this conversion by value of ImageWidth(0x0100) and ImageLength(0x0101).
    836      *
    837      * @param thumbnailBytes
    838      * @return
    839      * @throws com.drew.metadata.MetadataException
    840      * /
    841     private Image createImageFromRawYCbCr(byte[] thumbnailBytes) throws MetadataException
    842     {
    843         /*
    844             Y  =  0.257R + 0.504G + 0.098B + 16
    845             Cb = -0.148R - 0.291G + 0.439B + 128
    846             Cr =  0.439R - 0.368G - 0.071B + 128
    847 
    848             G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
    849             R = 1.164(Y-16) + 1.596(Cr-128)
    850             B = 1.164(Y-16) + 2.018(Cb-128)
    851 
    852             R, G and B range from 0 to 255.
    853             Y ranges from 16 to 235.
    854             Cb and Cr range from 16 to 240.
    855 
    856             http://www.faqs.org/faqs/graphics/colorspace-faq/
    857         * /
    858 
    859         int length = thumbnailBytes.length; // this.getInt(ExifDirectory.TAG_STRIP_BYTE_COUNTS);
    860         final int imageWidth = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
    861         final int imageHeight = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
    862 //        final int headerLength = 54;
    863 //        byte[] result = new byte[length + headerLength];
    864 //        // Add a windows BMP header described:
    865 //        // http://www.onicos.com/staff/iz/formats/bmp.html
    866 //        result[0] = 'B';
    867 //        result[1] = 'M'; // File Type identifier
    868 //        result[3] = (byte)(result.length / 256);
    869 //        result[2] = (byte)result.length;
    870 //        result[10] = (byte)headerLength;
    871 //        result[14] = 40; // MS Windows BMP header
    872 //        result[18] = (byte)imageWidth;
    873 //        result[22] = (byte)imageHeight;
    874 //        result[26] = 1;  // 1 Plane
    875 //        result[28] = 24; // Colour depth
    876 //        result[34] = (byte)length;
    877 //        result[35] = (byte)(length / 256);
    878 
    879         final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
    880 
    881         // order is YCbCr and image is upside down, bitmaps are BGR
    882 ////        for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)
    883 //        {
    884 //            final int y =  thumbnailBytes[dataOffset - 2] & 0xFF;
    885 //            final int cb = thumbnailBytes[dataOffset - 1] & 0xFF;
    886 //            final int cr = thumbnailBytes[dataOffset] & 0xFF;
    887 //            if (y<16 || y>235 || cb<16 || cb>240 || cr<16 || cr>240)
    888 //                "".toString();
    889 //
    890 //            int g = (int)(1.164*(y-16) - 0.391*(cb-128) - 0.813*(cr-128));
    891 //            int r = (int)(1.164*(y-16) + 1.596*(cr-128));
    892 //            int b = (int)(1.164*(y-16) + 2.018*(cb-128));
    893 //
    894 ////            result[i] = (byte)b;
    895 ////            result[i + 1] = (byte)g;
    896 ////            result[i + 2] = (byte)r;
    897 //
    898 //            // TODO compose the image here
    899 //            image.setRGB(1, 2, 3);
    900 //        }
    901 
    902         return image;
    903     }
    904 
    905     /**
    906      * Creates a thumbnail image in (Windows) BMP format from raw RGB data.
    907      * @param thumbnailBytes
    908      * @return
    909      * @throws com.drew.metadata.MetadataException
    910      * /
    911     private Image createImageFromRawRgb(byte[] thumbnailBytes) throws MetadataException
    912     {
    913         final int length = thumbnailBytes.length; // this.getInt(ExifDirectory.TAG_STRIP_BYTE_COUNTS);
    914         final int imageWidth = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
    915         final int imageHeight = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
    916 //        final int headerlength = 54;
    917 //        final byte[] result = new byte[length + headerlength];
    918 //        // Add a windows BMP header described:
    919 //        // http://www.onicos.com/staff/iz/formats/bmp.html
    920 //        result[0] = 'B';
    921 //        result[1] = 'M'; // File Type identifier
    922 //        result[3] = (byte)(result.length / 256);
    923 //        result[2] = (byte)result.length;
    924 //        result[10] = (byte)headerlength;
    925 //        result[14] = 40; // MS Windows BMP header
    926 //        result[18] = (byte)imageWidth;
    927 //        result[22] = (byte)imageHeight;
    928 //        result[26] = 1;  // 1 Plane
    929 //        result[28] = 24; // Colour depth
    930 //        result[34] = (byte)length;
    931 //        result[35] = (byte)(length / 256);
    932 
    933         final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
    934 
    935         // order is RGB and image is upside down, bitmaps are BGR
    936 //        for (int i = headerlength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)
    937 //        {
    938 //            byte b = thumbnailBytes[dataOffset - 2];
    939 //            byte g = thumbnailBytes[dataOffset - 1];
    940 //            byte r = thumbnailBytes[dataOffset];
    941 //
    942 //            // TODO compose the image here
    943 //            image.setRGB(1, 2, 3);
    944 //        }
    945 
    946         return image;
    947     }
    948 */
    949 
    950     public boolean containsThumbnail()
    951     {
    952         return containsTag(ExifDirectory.TAG_THUMBNAIL_DATA);
    953     }
    954 }
  • src/com/drew/metadata/exif/ExifIFD0Descriptor.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.Rational;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
     27import com.drew.metadata.TagDescriptor;
     28
     29import java.io.UnsupportedEncodingException;
     30
     31/**
     32 * Provides human-readable string representations of tag values stored in a <code>ExifIFD0Directory</code>.
     33 *
     34 * @author Drew Noakes http://drewnoakes.com
     35 */
     36public class ExifIFD0Descriptor extends TagDescriptor<ExifIFD0Directory>
     37{
     38    /**
     39     * Dictates whether rational values will be represented in decimal format in instances
     40     * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
     41     */
     42    private final boolean _allowDecimalRepresentationOfRationals = true;
     43
     44    public ExifIFD0Descriptor(@NotNull ExifIFD0Directory directory)
     45    {
     46        super(directory);
     47    }
     48
     49    // Note for the potential addition of brightness presentation in eV:
     50    // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv),
     51    // you must add SensitivityValue(Sv).
     52    // Ev=BV+Sv   Sv=log2(ISOSpeedRating/3.125)
     53    // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
     54
     55    /**
     56     * Returns a descriptive value of the the specified tag for this image.
     57     * Where possible, known values will be substituted here in place of the raw
     58     * tokens actually kept in the Exif segment.  If no substitution is
     59     * available, the value provided by getString(int) will be returned.
     60     * @param tagType the tag to find a description for
     61     * @return a description of the image's value for the specified tag, or
     62     *         <code>null</code> if the tag hasn't been defined.
     63     */
     64    @Nullable
     65    public String getDescription(int tagType)
     66    {
     67        switch (tagType) {
     68            case ExifIFD0Directory.TAG_RESOLUTION_UNIT:
     69                return getResolutionDescription();
     70            case ExifIFD0Directory.TAG_YCBCR_POSITIONING:
     71                return getYCbCrPositioningDescription();
     72            case ExifIFD0Directory.TAG_X_RESOLUTION:
     73                return getXResolutionDescription();
     74            case ExifIFD0Directory.TAG_Y_RESOLUTION:
     75                return getYResolutionDescription();
     76            case ExifIFD0Directory.TAG_REFERENCE_BLACK_WHITE:
     77                return getReferenceBlackWhiteDescription();
     78            case ExifIFD0Directory.TAG_ORIENTATION:
     79                return getOrientationDescription();
     80
     81            case ExifIFD0Directory.TAG_WIN_AUTHOR:
     82               return getWindowsAuthorDescription();
     83            case ExifIFD0Directory.TAG_WIN_COMMENT:
     84               return getWindowsCommentDescription();
     85            case ExifIFD0Directory.TAG_WIN_KEYWORDS:
     86               return getWindowsKeywordsDescription();
     87            case ExifIFD0Directory.TAG_WIN_SUBJECT:
     88               return getWindowsSubjectDescription();
     89            case ExifIFD0Directory.TAG_WIN_TITLE:
     90               return getWindowsTitleDescription();
     91
     92            default:
     93                return super.getDescription(tagType);
     94        }
     95    }
     96
     97    @Nullable
     98    public String getReferenceBlackWhiteDescription()
     99    {
     100        int[] ints = _directory.getIntArray(ExifIFD0Directory.TAG_REFERENCE_BLACK_WHITE);
     101        if (ints==null)
     102            return null;
     103        int blackR = ints[0];
     104        int whiteR = ints[1];
     105        int blackG = ints[2];
     106        int whiteG = ints[3];
     107        int blackB = ints[4];
     108        int whiteB = ints[5];
     109        return "[" + blackR + "," + blackG + "," + blackB + "] " +
     110               "[" + whiteR + "," + whiteG + "," + whiteB + "]";
     111    }
     112
     113    @Nullable
     114    public String getYResolutionDescription()
     115    {
     116        Rational value = _directory.getRational(ExifIFD0Directory.TAG_Y_RESOLUTION);
     117        if (value==null)
     118            return null;
     119        final String unit = getResolutionDescription();
     120        return value.toSimpleString(_allowDecimalRepresentationOfRationals) +
     121                " dots per " +
     122                (unit==null ? "unit" : unit.toLowerCase());
     123    }
     124
     125    @Nullable
     126    public String getXResolutionDescription()
     127    {
     128        Rational value = _directory.getRational(ExifIFD0Directory.TAG_X_RESOLUTION);
     129        if (value==null)
     130            return null;
     131        final String unit = getResolutionDescription();
     132        return value.toSimpleString(_allowDecimalRepresentationOfRationals) +
     133                " dots per " +
     134                (unit==null ? "unit" : unit.toLowerCase());
     135    }
     136
     137    @Nullable
     138    public String getYCbCrPositioningDescription()
     139    {
     140        Integer value = _directory.getInteger(ExifIFD0Directory.TAG_YCBCR_POSITIONING);
     141        if (value==null)
     142            return null;
     143        switch (value) {
     144            case 1: return "Center of pixel array";
     145            case 2: return "Datum point";
     146            default:
     147                return String.valueOf(value);
     148        }
     149    }
     150
     151    @Nullable
     152    public String getOrientationDescription()
     153    {
     154        Integer value = _directory.getInteger(ExifIFD0Directory.TAG_ORIENTATION);
     155        if (value==null)
     156            return null;
     157        switch (value) {
     158            case 1: return "Top, left side (Horizontal / normal)";
     159            case 2: return "Top, right side (Mirror horizontal)";
     160            case 3: return "Bottom, right side (Rotate 180)";
     161            case 4: return "Bottom, left side (Mirror vertical)";
     162            case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)";
     163            case 6: return "Right side, top (Rotate 90 CW)";
     164            case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)";
     165            case 8: return "Left side, bottom (Rotate 270 CW)";
     166            default:
     167                return String.valueOf(value);
     168        }
     169    }
     170
     171    @Nullable
     172    public String getResolutionDescription()
     173    {
     174        // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
     175        Integer value = _directory.getInteger(ExifIFD0Directory.TAG_RESOLUTION_UNIT);
     176        if (value==null)
     177            return null;
     178        switch (value) {
     179            case 1: return "(No unit)";
     180            case 2: return "Inch";
     181            case 3: return "cm";
     182            default:
     183                return "";
     184        }
     185    }
     186
     187    /** The Windows specific tags uses plain Unicode. */
     188    @Nullable
     189    private String getUnicodeDescription(int tag)
     190    {
     191         byte[] commentBytes = _directory.getByteArray(tag);
     192        if (commentBytes==null)
     193            return null;
     194         try {
     195             // Decode the unicode string and trim the unicode zero "\0" from the end.
     196            return new String(commentBytes, "UTF-16LE").trim();
     197         }
     198         catch (UnsupportedEncodingException ex) {
     199            return null;
     200         }
     201    }
     202
     203    @Nullable
     204    public String getWindowsAuthorDescription()
     205    {
     206       return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_AUTHOR);
     207    }
     208
     209    @Nullable
     210    public String getWindowsCommentDescription()
     211    {
     212       return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_COMMENT);
     213    }
     214
     215    @Nullable
     216    public String getWindowsKeywordsDescription()
     217    {
     218       return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_KEYWORDS);
     219    }
     220
     221    @Nullable
     222    public String getWindowsTitleDescription()
     223    {
     224       return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_TITLE);
     225    }
     226
     227    @Nullable
     228    public String getWindowsSubjectDescription()
     229    {
     230       return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_SUBJECT);
     231    }
     232}
  • src/com/drew/metadata/exif/ExifIFD0Descriptor.java

  • src/com/drew/metadata/exif/ExifIFD0Directory.java

    Modification de propriétés sur src/com/drew/metadata/exif/ExifIFD0Descriptor.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.metadata.Directory;
     26
     27import java.util.HashMap;
     28
     29/**
     30 * Describes Exif tags from the IFD0 directory.
     31 *
     32 * @author Drew Noakes http://drewnoakes.com
     33 */
     34public class ExifIFD0Directory extends Directory
     35{
     36    public static final int TAG_IMAGE_DESCRIPTION = 0x010E;
     37    public static final int TAG_MAKE = 0x010F;
     38    public static final int TAG_MODEL = 0x0110;
     39    public static final int TAG_ORIENTATION = 0x0112;
     40    public static final int TAG_X_RESOLUTION = 0x011A;
     41    public static final int TAG_Y_RESOLUTION = 0x011B;
     42    public static final int TAG_RESOLUTION_UNIT = 0x0128;
     43    public static final int TAG_SOFTWARE = 0x0131;
     44    public static final int TAG_DATETIME = 0x0132;
     45    public static final int TAG_ARTIST = 0x013B;
     46    public static final int TAG_WHITE_POINT = 0x013E;
     47    public static final int TAG_PRIMARY_CHROMATICITIES = 0x013F;
     48    public static final int TAG_YCBCR_COEFFICIENTS = 0x0211;
     49    public static final int TAG_YCBCR_POSITIONING = 0x0213;
     50    public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214;
     51    public static final int TAG_COPYRIGHT = 0x8298;
     52
     53    /** The image title, as used by Windows XP. */
     54    public static final int TAG_WIN_TITLE = 0x9C9B;
     55    /** The image comment, as used by Windows XP. */
     56    public static final int TAG_WIN_COMMENT = 0x9C9C;
     57    /** The image author, as used by Windows XP (called Artist in the Windows shell). */
     58    public static final int TAG_WIN_AUTHOR = 0x9C9D;
     59    /** The image keywords, as used by Windows XP. */
     60    public static final int TAG_WIN_KEYWORDS = 0x9C9E;
     61    /** The image subject, as used by Windows XP. */
     62    public static final int TAG_WIN_SUBJECT = 0x9C9F;
     63
     64    @NotNull
     65    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     66
     67    static
     68    {
     69        _tagNameMap.put(TAG_IMAGE_DESCRIPTION, "Image Description");
     70        _tagNameMap.put(TAG_MAKE, "Make");
     71        _tagNameMap.put(TAG_MODEL, "Model");
     72        _tagNameMap.put(TAG_ORIENTATION, "Orientation");
     73        _tagNameMap.put(TAG_X_RESOLUTION, "X Resolution");
     74        _tagNameMap.put(TAG_Y_RESOLUTION, "Y Resolution");
     75        _tagNameMap.put(TAG_RESOLUTION_UNIT, "Resolution Unit");
     76        _tagNameMap.put(TAG_SOFTWARE, "Software");
     77        _tagNameMap.put(TAG_DATETIME, "Date/Time");
     78        _tagNameMap.put(TAG_ARTIST, "Artist");
     79        _tagNameMap.put(TAG_WHITE_POINT, "White Point");
     80        _tagNameMap.put(TAG_PRIMARY_CHROMATICITIES, "Primary Chromaticities");
     81        _tagNameMap.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients");
     82        _tagNameMap.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning");
     83        _tagNameMap.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White");
     84        _tagNameMap.put(TAG_COPYRIGHT, "Copyright");
     85
     86        _tagNameMap.put(TAG_WIN_AUTHOR, "Windows XP Author");
     87        _tagNameMap.put(TAG_WIN_COMMENT, "Windows XP Comment");
     88        _tagNameMap.put(TAG_WIN_KEYWORDS, "Windows XP Keywords");
     89        _tagNameMap.put(TAG_WIN_SUBJECT, "Windows XP Subject");
     90        _tagNameMap.put(TAG_WIN_TITLE, "Windows XP Title");
     91    }
     92
     93    public ExifIFD0Directory()
     94    {
     95        this.setDescriptor(new ExifIFD0Descriptor(this));
     96    }
     97
     98    @NotNull
     99    public String getName()
     100    {
     101        return "Exif IFD0";
     102    }
     103
     104    @NotNull
     105    protected HashMap<Integer, String> getTagNameMap()
     106    {
     107        return _tagNameMap;
     108    }
     109}
  • src/com/drew/metadata/exif/ExifIFD0Directory.java

  • src/com/drew/metadata/exif/ExifInteropDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/exif/ExifIFD0Directory.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    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) {
    3643            case ExifInteropDirectory.TAG_INTEROP_INDEX:
     
    3845            case ExifInteropDirectory.TAG_INTEROP_VERSION:
    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}
  • src/com/drew/metadata/exif/ExifInteropDirectory.java

     
    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
    2126import java.util.HashMap;
    2227
    2328/**
     29 * Describes Exif interoperability tags.
    2430 *
     31 * @author Drew Noakes http://drewnoakes.com
    2532 */
    2633public class ExifInteropDirectory extends Directory
    2734{
     
    3138    public static final int TAG_RELATED_IMAGE_WIDTH = 0x1001;
    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
    4653    public ExifInteropDirectory()
     
    4855        this.setDescriptor(new ExifInteropDescriptor(this));
    4956    }
    5057
     58    @NotNull
    5159    public String getName()
    5260    {
    5361        return "Interoperability";
    5462    }
    5563
    56     protected HashMap getTagNameMap()
     64    @NotNull
     65    protected HashMap<Integer, String> getTagNameMap()
    5766    {
    58         return tagNameMap;
     67        return _tagNameMap;
    5968    }
    6069}
  • src/com/drew/metadata/exif/ExifProcessingException.java

     
    1 /*
    2  * ExifProcessingException.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.  I do ask that you leave this header in tact.
    8  *
    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.
    11  *
    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/
    16  *
    17  * Created on 29 April 2002, 00:33
    18  */
    19 package com.drew.metadata.exif;
    20 
    21 import com.drew.metadata.MetadataException;
    22 
    23 /**
    24  * The exception type raised during reading of Exif data in the instance of
    25  * unexpected data conditions.
    26  * @author  Drew Noakes http://drewnoakes.com
    27  */
    28 public class ExifProcessingException extends MetadataException
    29 {
    30     /**
    31      * Constructs an instance of <code>ExifProcessingException</code> with the
    32      * specified detail message.
    33      * @param message the detail message
    34      */
    35     public ExifProcessingException(String message)
    36     {
    37         super(message);
    38     }
    39 
    40     /**
    41      * Constructs an instance of <code>ExifProcessingException</code> with the
    42      * specified detail message and inner exception.
    43      * @param message the detail message
    44      * @param cause an inner exception
    45      */
    46     public ExifProcessingException(String message, Throwable cause)
    47     {
    48         super(message, cause);
    49     }
    50 }
  • src/com/drew/metadata/exif/ExifReader.java

     
    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;
     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 };
    7047
    71     /**
    72      * Represents the native byte ordering used in the JPEG segment.  If true,
    73      * then we're using Motorolla ordering (Big endian), else we're using Intel
    74      * ordering (Little endian).
    75      */
    76     private boolean _isMotorollaByteOrder;
    77 
    78     /**
    79      * Bean instance to store information about the image and camera/scanner/capture
    80      * device.
    81      */
    82     private Metadata _metadata;
    83 
    84     /**
    85      * The number of bytes used per format descriptor.
    86      */
    87     private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
    88 
    89     /**
    90      * The number of formats known.
    91      */
     48    /** The number of formats known. */
    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;
    11685
    11786    /**
    118      * Creates an ExifReader for a JpegSegmentData object.
    119      * @param segmentData
     87     * Performs the Exif data extraction, adding found values to the specified
     88     * instance of <code>Metadata</code>.
     89     *
     90     * @param reader   The buffer reader from which Exif data should be read.
     91     * @param metadata The Metadata object into which extracted values should be merged.
    12092     */
    121     public ExifReader(JpegSegmentData segmentData)
     93    public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
    12294    {
    123         this(segmentData.getSegment(JpegSegmentReader.SEGMENT_APP1));
    124     }
     95        final ExifSubIFDDirectory directory = metadata.getOrCreateDirectory(ExifSubIFDDirectory.class);
    12596
    126     /**
    127      * Creates an ExifReader for a Jpeg file.
    128      * @param file
    129      * @throws JpegProcessingException
    130      */
    131     public ExifReader(File file) throws JpegProcessingException
    132     {
    133         this(new JpegSegmentReader(file).readSegment(JpegSegmentReader.SEGMENT_APP1));
    134     }
     97        // check for the header length
     98        if (reader.getLength() <= 14) {
     99            directory.addError("Exif data segment must contain at least 14 bytes");
     100            return;
     101        }
    135102
    136     /**
    137      * Creates an ExifReader for a Jpeg stream.
    138      * @param is JPEG stream. Stream will be closed.
    139      */
    140     public ExifReader(InputStream is) throws JpegProcessingException
    141     {
    142         this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APP1));
    143     }
     103        // check for the header preamble
     104        try {
     105            if (!reader.getString(0, 6).equals("Exif\0\0")) {
     106                directory.addError("Exif data segment doesn't begin with 'Exif'");
     107                return;
     108            }
    144109
    145     /**
    146      * Creates an ExifReader for the given JPEG header segment.
    147      */
    148     public ExifReader(byte[] data)
    149     {
    150         _data = data;
     110            extractIFD(metadata, metadata.getOrCreateDirectory(ExifIFD0Directory.class), TIFF_HEADER_START_OFFSET, reader);
     111        } catch (BufferBoundsException e) {
     112            directory.addError("Exif data segment ended prematurely");
     113        }
    151114    }
    152115
    153116    /**
    154      * Performs the Exif data extraction, returning a new instance of <code>Metadata</code>.
    155      */
    156     public Metadata extract()
    157     {
    158         return extract(new Metadata());
    159     }
    160 
    161     /**
    162      * Performs the Exif data extraction, adding found values to the specified
     117     * Performs the Exif data extraction on a TIFF/RAW, adding found values to the specified
    163118     * 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.
    164122     */
    165     public Metadata extract(Metadata metadata)
     123    public void extractTiff(@NotNull BufferReader reader, @NotNull Metadata metadata)
    166124    {
    167         _metadata = metadata;
    168         if (_data==null)
    169             return _metadata;
     125        final ExifIFD0Directory directory = metadata.getOrCreateDirectory(ExifIFD0Directory.class);
    170126
    171         // once we know there's some data, create the directory and start working on it
    172         ExifDirectory directory = (ExifDirectory)_metadata.getDirectory(ExifDirectory.class);
    173 
    174         // check for the header length
    175         if (_data.length<=14) {
    176             directory.addError("Exif data segment must contain at least 14 bytes");
    177             return _metadata;
     127        try {
     128            extractIFD(metadata, directory, 0, reader);
     129        } catch (BufferBoundsException e) {
     130            directory.addError("Exif data segment ended prematurely");
    178131        }
     132    }
    179133
    180         // check for the header preamble
    181         if (!"Exif\0\0".equals(new String(_data, 0, 6))) {
    182             directory.addError("Exif data segment doesn't begin with 'Exif'");
    183             return _metadata;
    184         }
     134    private void extractIFD(@NotNull Metadata metadata, @NotNull final ExifIFD0Directory directory, int tiffHeaderOffset, @NotNull BufferReader reader) throws BufferBoundsException
     135    {
     136        // this should be either "MM" or "II"
     137        String byteOrderIdentifier = reader.getString(tiffHeaderOffset, 2);
    185138
    186         // this should be either "MM" or "II"
    187         String byteOrderIdentifier = new String(_data, 6, 2);
    188         if (!setByteOrder(byteOrderIdentifier)) {
     139        if ("MM".equals(byteOrderIdentifier)) {
     140            reader.setMotorolaByteOrder(true);
     141        } else if ("II".equals(byteOrderIdentifier)) {
     142            reader.setMotorolaByteOrder(false);
     143        } else {
    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;
     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;
    197158        }
    198159
    199         int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET;
     160        int firstDirectoryOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset;
    200161
    201         // David Ekholm sent an digital camera image that has this problem
    202         if (firstDirectoryOffset>=_data.length - 1) {
     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
    205166            firstDirectoryOffset = 14;
    206167        }
    207168
    208         HashMap processedDirectoryOffsets = new HashMap();
     169        Set<Integer> processedDirectoryOffsets = new HashSet<Integer>();
    209170
    210         // 0th IFD (we merge with Exif IFD)
    211         processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET);
     171        processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, tiffHeaderOffset, metadata, reader);
    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];
     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                }
    234185            }
    235             exifDirectory.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);
    236         } catch (Throwable e) {
    237             exifDirectory.addError("Unable to extract thumbnail: " + e.getMessage());
    238186        }
    239187    }
    240188
    241     private boolean setByteOrder(String byteOrderIdentifier)
    242     {
    243         if ("MM".equals(byteOrderIdentifier)) {
    244             _isMotorollaByteOrder = true;
    245         } else if ("II".equals(byteOrderIdentifier)) {
    246             _isMotorollaByteOrder = false;
    247         } else {
    248             return false;
    249         }
    250         return true;
    251     }
    252 
    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");
     207        processedDirectoryOffsets.add(dirStartOffset);
    269208
    270         if (dirStartOffset>=_data.length || dirStartOffset<0) {
    271             directory.addError("Ignored directory marked to start outside data segement");
     209        if (dirStartOffset >= reader.getLength() || dirStartOffset < 0) {
     210            directory.addError("Ignored directory marked to start outside data segment");
    272211            return;
    273212        }
    274213
    275         if (!isDirectoryLengthValid(dirStartOffset, tiffHeaderOffset)) {
     214        // First two bytes in the IFD are the number of tags in this directory
     215        int dirTagCount = reader.getUInt16(dirStartOffset);
     216
     217        int dirLength = (2 + (12 * dirTagCount) + 4);
     218        if (dirLength + dirStartOffset > reader.getLength()) {
    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            }
    311268
    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        }
    341302
    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
    350311                return;
     
    352313                // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
    353314                return;
    354315            }
    355             // the next directory is of same type as this one
    356             processDirectory(directory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset);
     316            // TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case
     317            final ExifThumbnailDirectory nextDirectory = metadata.getOrCreateDirectory(ExifThumbnailDirectory.class);
     318            processDirectory(nextDirectory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader);
    357319        }
    358320    }
    359321
    360     private void processMakerNote(int subdirOffset, HashMap processedDirectoryOffsets, int tiffHeaderOffset)
     322    private void processMakerNote(int subdirOffset, @NotNull Set<Integer> processedDirectoryOffsets, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull BufferReader reader) throws BufferBoundsException
    361323    {
    362324        // Determine the camera model and makernote format
    363         Directory exifDirectory = _metadata.getDirectory(ExifDirectory.class);
     325        Directory ifd0Directory = metadata.getDirectory(ExifIFD0Directory.class);
    364326
    365         if (exifDirectory==null)
     327        if (ifd0Directory==null)
    366328            return;
    367329
    368         String cameraModel = exifDirectory.getString(ExifDirectory.TAG_MAKE);
    369         final String firstTwoChars = new String(_data, subdirOffset, 2);
    370         final String firstThreeChars = new String(_data, subdirOffset, 3);
    371         final String firstFourChars = new String(_data, subdirOffset, 4);
    372         final String firstFiveChars = new String(_data, subdirOffset, 5);
    373         final String firstSixChars = new String(_data, subdirOffset, 6);
    374         final String firstSevenChars = new String(_data, subdirOffset, 7);
    375         final String firstEightChars = new String(_data, subdirOffset, 8);
    376         if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars))
    377         {
     330        String cameraModel = ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE);
     331
     332        //final String firstTwoChars = reader.getString(subdirOffset, 2);
     333        final String firstThreeChars = reader.getString(subdirOffset, 3);
     334        final String firstFourChars = reader.getString(subdirOffset, 4);
     335        final String firstFiveChars = reader.getString(subdirOffset, 5);
     336        final String firstSixChars = reader.getString(subdirOffset, 6);
     337        final String firstSevenChars = reader.getString(subdirOffset, 7);
     338        final String firstEightChars = reader.getString(subdirOffset, 8);
     339        final String firstTwelveChars = reader.getString(subdirOffset, 12);
     340
     341        if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) {
    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:                  **
    389349                 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
     
    392352                 * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
    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             }
    402             else
    403             {
     355                switch (reader.getUInt8(subdirOffset + 6)) {
     356                    case 1:
     357                        processDirectory(metadata.getOrCreateDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader);
     358                        break;
     359                    case 2:
     360                        processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10, metadata, reader);
     361                        break;
     362                    default:
     363                        ifd0Directory.addError("Unsupported Nikon makernote data ignored.");
     364                        break;
     365                }
     366            } else {
    404367                // The IFD begins with the first MakerNote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
    405                 processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
     368                processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
    406369            }
    407         }
    408         else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars))
    409         {
    410             processDirectory(_metadata.getDirectory(SonyMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset);
    411         }
    412         else if ("KDK".equals(firstThreeChars))
    413         {
    414             processDirectory(_metadata.getDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset);
    415         }
    416         else if ("Canon".equalsIgnoreCase(cameraModel))
    417         {
    418             processDirectory(_metadata.getDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
    419         }
    420         else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("CASIO"))
    421         {
     370        } else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars)) {
     371            processDirectory(metadata.getOrCreateDirectory(SonyType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader);
     372        } else if ("SIGMA\u0000\u0000\u0000".equals(firstEightChars) || "FOVEON\u0000\u0000".equals(firstEightChars)) {
     373            processDirectory(metadata.getOrCreateDirectory(SigmaMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 10, tiffHeaderOffset, metadata, reader);
     374        } else if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".equals(firstTwelveChars)) {
     375            // force MM for this directory
     376            boolean isMotorola = reader.isMotorolaByteOrder();
     377            reader.setMotorolaByteOrder(true);
     378            // skip 12 byte header + 2 for "MM" + 6
     379            processDirectory(metadata.getOrCreateDirectory(SonyType6MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader);
     380            reader.setMotorolaByteOrder(isMotorola);
     381        } else if ("KDK".equals(firstThreeChars)) {
     382            processDirectory(metadata.getOrCreateDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader);
     383        } else if ("Canon".equalsIgnoreCase(cameraModel)) {
     384            processDirectory(metadata.getOrCreateDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader);
     385        } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("CASIO")) {
    422386            if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
    423                 processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset);
     387                processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset, metadata, reader);
    424388            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
    469415            // Offsets are relative to the start of the current IFD tag, not the TIFF header
    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
    478422            // Offsets are relative to the start of the current IFD tag, not the TIFF header
    479423            // Observed for:
    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.");
    489435        }
    490436    }
    491437
    492     private boolean isDirectoryLengthValid(int dirStartOffset, int tiffHeaderOffset)
     438    private void processTag(@NotNull Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, @NotNull final BufferReader reader) throws BufferBoundsException
    493439    {
    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)
    504     {
    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                }
    568561                break;
    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                }
    580574                break;
     
    583577        }
    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 
    604580    /**
    605      * Creates a String from the _data buffer starting at the specified offset,
    606      * and ending where byte=='\0' or where length==maxLength.
    607      */
    608     private String readString(int offset, int maxLength)
    609     {
    610         int length = 0;
    611         while ((offset + length)<_data.length && _data[offset + length]!='\0' && length<maxLength)
    612             length++;
    613 
    614         return new String(_data, offset, length);
    615     }
    616 
    617     /**
    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)
    623587    {
     
    625589        // each entry is 12 bytes, so we skip 12 * the number seen so far
    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}
  • src/com/drew/metadata/exif/ExifSubIFDDescriptor.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.imaging.PhotographicConversions;
     24import com.drew.lang.Rational;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
     27import com.drew.metadata.TagDescriptor;
     28
     29import java.io.UnsupportedEncodingException;
     30import java.text.DecimalFormat;
     31import java.util.HashMap;
     32import java.util.Map;
     33
     34/**
     35 * Provides human-readable string representations of tag values stored in a <code>ExifSubIFDDirectory</code>.
     36 *
     37 * @author Drew Noakes http://drewnoakes.com
     38 */
     39public class ExifSubIFDDescriptor extends TagDescriptor<ExifSubIFDDirectory>
     40{
     41    /**
     42     * Dictates whether rational values will be represented in decimal format in instances
     43     * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
     44     */
     45    private final boolean _allowDecimalRepresentationOfRationals = true;
     46
     47    @NotNull
     48    private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#");
     49
     50    public ExifSubIFDDescriptor(@NotNull ExifSubIFDDirectory directory)
     51    {
     52        super(directory);
     53    }
     54
     55    // Note for the potential addition of brightness presentation in eV:
     56    // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv),
     57    // you must add SensitivityValue(Sv).
     58    // Ev=BV+Sv   Sv=log2(ISOSpeedRating/3.125)
     59    // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
     60
     61    /**
     62     * Returns a descriptive value of the the specified tag for this image.
     63     * Where possible, known values will be substituted here in place of the raw
     64     * tokens actually kept in the Exif segment.  If no substitution is
     65     * available, the value provided by getString(int) will be returned.
     66     * @param tagType the tag to find a description for
     67     * @return a description of the image's value for the specified tag, or
     68     *         <code>null</code> if the tag hasn't been defined.
     69     */
     70    @Nullable
     71    public String getDescription(int tagType)
     72    {
     73        switch (tagType) {
     74            case ExifSubIFDDirectory.TAG_NEW_SUBFILE_TYPE:
     75                return getNewSubfileTypeDescription();
     76            case ExifSubIFDDirectory.TAG_SUBFILE_TYPE:
     77                return getSubfileTypeDescription();
     78            case ExifSubIFDDirectory.TAG_THRESHOLDING:
     79                return getThresholdingDescription();
     80            case ExifSubIFDDirectory.TAG_FILL_ORDER:
     81                return getFillOrderDescription();
     82            case ExifSubIFDDirectory.TAG_EXPOSURE_TIME:
     83                return getExposureTimeDescription();
     84            case ExifSubIFDDirectory.TAG_SHUTTER_SPEED:
     85                return getShutterSpeedDescription();
     86            case ExifSubIFDDirectory.TAG_FNUMBER:
     87                return getFNumberDescription();
     88            case ExifSubIFDDirectory.TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL:
     89                return getCompressedAverageBitsPerPixelDescription();
     90            case ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE:
     91                return getSubjectDistanceDescription();
     92            case ExifSubIFDDirectory.TAG_METERING_MODE:
     93                return getMeteringModeDescription();
     94            case ExifSubIFDDirectory.TAG_WHITE_BALANCE:
     95                return getWhiteBalanceDescription();
     96            case ExifSubIFDDirectory.TAG_FLASH:
     97                return getFlashDescription();
     98            case ExifSubIFDDirectory.TAG_FOCAL_LENGTH:
     99                return getFocalLengthDescription();
     100            case ExifSubIFDDirectory.TAG_COLOR_SPACE:
     101                return getColorSpaceDescription();
     102            case ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH:
     103                return getExifImageWidthDescription();
     104            case ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT:
     105                return getExifImageHeightDescription();
     106            case ExifSubIFDDirectory.TAG_FOCAL_PLANE_UNIT:
     107                return getFocalPlaneResolutionUnitDescription();
     108            case ExifSubIFDDirectory.TAG_FOCAL_PLANE_X_RES:
     109                return getFocalPlaneXResolutionDescription();
     110            case ExifSubIFDDirectory.TAG_FOCAL_PLANE_Y_RES:
     111                return getFocalPlaneYResolutionDescription();
     112            case ExifSubIFDDirectory.TAG_BITS_PER_SAMPLE:
     113                return getBitsPerSampleDescription();
     114            case ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION:
     115                return getPhotometricInterpretationDescription();
     116            case ExifSubIFDDirectory.TAG_ROWS_PER_STRIP:
     117                return getRowsPerStripDescription();
     118            case ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS:
     119                return getStripByteCountsDescription();
     120            case ExifSubIFDDirectory.TAG_SAMPLES_PER_PIXEL:
     121                return getSamplesPerPixelDescription();
     122            case ExifSubIFDDirectory.TAG_PLANAR_CONFIGURATION:
     123                return getPlanarConfigurationDescription();
     124            case ExifSubIFDDirectory.TAG_YCBCR_SUBSAMPLING:
     125                return getYCbCrSubsamplingDescription();
     126            case ExifSubIFDDirectory.TAG_EXPOSURE_PROGRAM:
     127                return getExposureProgramDescription();
     128            case ExifSubIFDDirectory.TAG_APERTURE:
     129                return getApertureValueDescription();
     130            case ExifSubIFDDirectory.TAG_MAX_APERTURE:
     131                return getMaxApertureValueDescription();
     132            case ExifSubIFDDirectory.TAG_SENSING_METHOD:
     133                return getSensingMethodDescription();
     134            case ExifSubIFDDirectory.TAG_EXPOSURE_BIAS:
     135                return getExposureBiasDescription();
     136            case ExifSubIFDDirectory.TAG_FILE_SOURCE:
     137                return getFileSourceDescription();
     138            case ExifSubIFDDirectory.TAG_SCENE_TYPE:
     139                return getSceneTypeDescription();
     140            case ExifSubIFDDirectory.TAG_COMPONENTS_CONFIGURATION:
     141                return getComponentConfigurationDescription();
     142            case ExifSubIFDDirectory.TAG_EXIF_VERSION:
     143                return getExifVersionDescription();
     144            case ExifSubIFDDirectory.TAG_FLASHPIX_VERSION:
     145                return getFlashPixVersionDescription();
     146            case ExifSubIFDDirectory.TAG_ISO_EQUIVALENT:
     147                return getIsoEquivalentDescription();
     148            case ExifSubIFDDirectory.TAG_USER_COMMENT:
     149                return getUserCommentDescription();
     150            case ExifSubIFDDirectory.TAG_CUSTOM_RENDERED:
     151                return getCustomRenderedDescription();
     152            case ExifSubIFDDirectory.TAG_EXPOSURE_MODE:
     153                return getExposureModeDescription();
     154            case ExifSubIFDDirectory.TAG_WHITE_BALANCE_MODE:
     155                return getWhiteBalanceModeDescription();
     156            case ExifSubIFDDirectory.TAG_DIGITAL_ZOOM_RATIO:
     157                return getDigitalZoomRatioDescription();
     158            case ExifSubIFDDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH:
     159                return get35mmFilmEquivFocalLengthDescription();
     160            case ExifSubIFDDirectory.TAG_SCENE_CAPTURE_TYPE:
     161                return getSceneCaptureTypeDescription();
     162            case ExifSubIFDDirectory.TAG_GAIN_CONTROL:
     163                return getGainControlDescription();
     164            case ExifSubIFDDirectory.TAG_CONTRAST:
     165                return getContrastDescription();
     166            case ExifSubIFDDirectory.TAG_SATURATION:
     167                return getSaturationDescription();
     168            case ExifSubIFDDirectory.TAG_SHARPNESS:
     169                return getSharpnessDescription();
     170            case ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE_RANGE:
     171                return getSubjectDistanceRangeDescription();
     172            default:
     173                return super.getDescription(tagType);
     174        }
     175    }
     176
     177    @Nullable
     178    public String getNewSubfileTypeDescription()
     179    {
     180        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_NEW_SUBFILE_TYPE);
     181        if (value==null)
     182            return null;
     183        switch (value) {
     184            case 1: return "Full-resolution image";
     185            case 2: return "Reduced-resolution image";
     186            case 3: return "Single page of multi-page reduced-resolution image";
     187            case 4: return "Transparency mask";
     188            case 5: return "Transparency mask of reduced-resolution image";
     189            case 6: return "Transparency mask of multi-page image";
     190            case 7: return "Transparency mask of reduced-resolution multi-page image";
     191            default:
     192                return "Unknown (" + value + ")";
     193        }
     194    }
     195
     196    @Nullable
     197    public String getSubfileTypeDescription()
     198    {
     199        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SUBFILE_TYPE);
     200        if (value==null)
     201            return null;
     202        switch (value) {
     203            case 1: return "Full-resolution image";
     204            case 2: return "Reduced-resolution image";
     205            case 3: return "Single page of multi-page image";
     206            default:
     207                return "Unknown (" + value + ")";
     208        }
     209    }
     210
     211    @Nullable
     212    public String getThresholdingDescription()
     213    {
     214        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_THRESHOLDING);
     215        if (value==null)
     216            return null;
     217        switch (value) {
     218            case 1: return "No dithering or halftoning";
     219            case 2: return "Ordered dither or halftone";
     220            case 3: return "Randomized dither";
     221            default:
     222                return "Unknown (" + value + ")";
     223        }
     224    }
     225
     226    @Nullable
     227    public String getFillOrderDescription()
     228    {
     229        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_FILL_ORDER);
     230        if (value==null)
     231            return null;
     232        switch (value) {
     233            case 1: return "Normal";
     234            case 2: return "Reversed";
     235            default:
     236                return "Unknown (" + value + ")";
     237        }
     238    }
     239
     240    @Nullable
     241    public String getSubjectDistanceRangeDescription()
     242    {
     243        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE_RANGE);
     244        if (value==null)
     245            return null;
     246        switch (value) {
     247            case 0: return "Unknown";
     248            case 1: return "Macro";
     249            case 2: return "Close view";
     250            case 3: return "Distant view";
     251            default:
     252                return "Unknown (" + value + ")";
     253        }
     254    }
     255
     256    @Nullable
     257    public String getSharpnessDescription()
     258    {
     259        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SHARPNESS);
     260        if (value==null)
     261            return null;
     262        switch (value) {
     263            case 0: return "None";
     264            case 1: return "Low";
     265            case 2: return "Hard";
     266            default:
     267                return "Unknown (" + value + ")";
     268        }
     269    }
     270
     271    @Nullable
     272    public String getSaturationDescription()
     273    {
     274        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SATURATION);
     275        if (value==null)
     276            return null;
     277        switch (value) {
     278            case 0: return "None";
     279            case 1: return "Low saturation";
     280            case 2: return "High saturation";
     281            default:
     282                return "Unknown (" + value + ")";
     283        }
     284    }
     285
     286    @Nullable
     287    public String getContrastDescription()
     288    {
     289        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_CONTRAST);
     290        if (value==null)
     291            return null;
     292        switch (value) {
     293            case 0: return "None";
     294            case 1: return "Soft";
     295            case 2: return "Hard";
     296            default:
     297                return "Unknown (" + value + ")";
     298        }
     299    }
     300
     301    @Nullable
     302    public String getGainControlDescription()
     303    {
     304        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_GAIN_CONTROL);
     305        if (value==null)
     306            return null;
     307        switch (value) {
     308            case 0: return "None";
     309            case 1: return "Low gain up";
     310            case 2: return "Low gain down";
     311            case 3: return "High gain up";
     312            case 4: return "High gain down";
     313            default:
     314                return "Unknown (" + value + ")";
     315        }
     316    }
     317
     318    @Nullable
     319    public String getSceneCaptureTypeDescription()
     320    {
     321        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SCENE_CAPTURE_TYPE);
     322        if (value==null)
     323            return null;
     324        switch (value) {
     325            case 0: return "Standard";
     326            case 1: return "Landscape";
     327            case 2: return "Portrait";
     328            case 3: return "Night scene";
     329            default:
     330                return "Unknown (" + value + ")";
     331        }
     332    }
     333
     334    @Nullable
     335    public String get35mmFilmEquivFocalLengthDescription()
     336    {
     337        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH);
     338        if (value==null)
     339            return null;
     340        if (value==0)
     341            return "Unknown";
     342        else
     343            return SimpleDecimalFormatter.format(value) + "mm";
     344    }
     345
     346    @Nullable
     347    public String getDigitalZoomRatioDescription()
     348    {
     349        Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_DIGITAL_ZOOM_RATIO);
     350        if (value==null)
     351            return null;
     352        if (value.getNumerator()==0)
     353            return "Digital zoom not used.";
     354        return SimpleDecimalFormatter.format(value.doubleValue());
     355    }
     356
     357    @Nullable
     358    public String getWhiteBalanceModeDescription()
     359    {
     360        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_WHITE_BALANCE_MODE);
     361        if (value==null)
     362            return null;
     363        switch (value) {
     364            case 0: return "Auto white balance";
     365            case 1: return "Manual white balance";
     366            default:
     367                return "Unknown (" + value + ")";
     368        }
     369    }
     370
     371    @Nullable
     372    public String getExposureModeDescription()
     373    {
     374        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXPOSURE_MODE);
     375        if (value==null)
     376            return null;
     377        switch (value) {
     378            case 0: return "Auto exposure";
     379            case 1: return "Manual exposure";
     380            case 2: return "Auto bracket";
     381            default:
     382                return "Unknown (" + value + ")";
     383        }
     384    }
     385
     386    @Nullable
     387    public String getCustomRenderedDescription()
     388    {
     389        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_CUSTOM_RENDERED);
     390        if (value==null)
     391            return null;
     392        switch (value) {
     393            case 0: return "Normal process";
     394            case 1: return "Custom process";
     395            default:
     396                return "Unknown (" + value + ")";
     397        }
     398    }
     399
     400    @Nullable
     401    public String getUserCommentDescription()
     402    {
     403        byte[] commentBytes = _directory.getByteArray(ExifSubIFDDirectory.TAG_USER_COMMENT);
     404        if (commentBytes==null)
     405            return null;
     406        if (commentBytes.length == 0)
     407            return "";
     408
     409        final Map<String, String> encodingMap = new HashMap<String, String>();
     410        encodingMap.put("ASCII",    System.getProperty("file.encoding")); // Someone suggested "ISO-8859-1".
     411        encodingMap.put("UNICODE",  "UTF-16LE");
     412        encodingMap.put("JIS",      "Shift-JIS"); // We assume this charset for now.  Another suggestion is "JIS".
     413
     414        try {
     415            if (commentBytes.length >= 10) {
     416                String firstTenBytesString = new String(commentBytes, 0, 10);
     417
     418                // try each encoding name
     419                for (Map.Entry<String, String> pair : encodingMap.entrySet()) {
     420                    String encodingName = pair.getKey();
     421                    String charset = pair.getValue();
     422                    if (firstTenBytesString.startsWith(encodingName)) {
     423                        // skip any null or blank characters commonly present after the encoding name, up to a limit of 10 from the start
     424                        for (int j = encodingName.length(); j < 10; j++) {
     425                            byte b = commentBytes[j];
     426                            if (b != '\0' && b != ' ')
     427                                return new String(commentBytes, j, commentBytes.length - j, charset).trim();
     428                        }
     429                        return new String(commentBytes, 10, commentBytes.length - 10, charset).trim();
     430                    }
     431                }
     432            }
     433            // special handling fell through, return a plain string representation
     434            return new String(commentBytes, System.getProperty("file.encoding")).trim();
     435        } catch (UnsupportedEncodingException ex) {
     436            return null;
     437        }
     438    }
     439
     440    @Nullable
     441    public String getIsoEquivalentDescription()
     442    {
     443        // Have seen an exception here from files produced by ACDSEE that stored an int[] here with two values
     444        Integer isoEquiv = _directory.getInteger(ExifSubIFDDirectory.TAG_ISO_EQUIVALENT);
     445        if (isoEquiv==null)
     446            return null;
     447        // There used to be a check here that multiplied ISO values < 50 by 200.
     448        // Issue 36 shows a smart-phone image from a Samsung Galaxy S2 with ISO-40.
     449        return Integer.toString(isoEquiv);
     450    }
     451
     452    @Nullable
     453    public String getExifVersionDescription()
     454    {
     455        int[] ints = _directory.getIntArray(ExifSubIFDDirectory.TAG_EXIF_VERSION);
     456        if (ints==null)
     457            return null;
     458        return ExifSubIFDDescriptor.convertBytesToVersionString(ints, 2);
     459    }
     460
     461    @Nullable
     462    public String getFlashPixVersionDescription()
     463    {
     464        int[] ints = _directory.getIntArray(ExifSubIFDDirectory.TAG_FLASHPIX_VERSION);
     465        if (ints==null)
     466            return null;
     467        return ExifSubIFDDescriptor.convertBytesToVersionString(ints, 2);
     468    }
     469
     470    @Nullable
     471    public String getSceneTypeDescription()
     472    {
     473        Integer sceneType = _directory.getInteger(ExifSubIFDDirectory.TAG_SCENE_TYPE);
     474        if (sceneType==null)
     475            return null;
     476        return sceneType == 1
     477                ? "Directly photographed image"
     478                : "Unknown (" + sceneType + ")";
     479    }
     480
     481    @Nullable
     482    public String getFileSourceDescription()
     483    {
     484        Integer fileSource = _directory.getInteger(ExifSubIFDDirectory.TAG_FILE_SOURCE);
     485        if (fileSource==null)
     486            return null;
     487        return fileSource == 3
     488                ? "Digital Still Camera (DSC)"
     489                : "Unknown (" + fileSource + ")";
     490    }
     491
     492    @Nullable
     493    public String getExposureBiasDescription()
     494    {
     495        Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_EXPOSURE_BIAS);
     496        if (value==null)
     497            return null;
     498        return value.toSimpleString(true) + " EV";
     499    }
     500
     501    @Nullable
     502    public String getMaxApertureValueDescription()
     503    {
     504        Double aperture = _directory.getDoubleObject(ExifSubIFDDirectory.TAG_MAX_APERTURE);
     505        if (aperture==null)
     506            return null;
     507        double fStop = PhotographicConversions.apertureToFStop(aperture);
     508        return "F" + SimpleDecimalFormatter.format(fStop);
     509    }
     510
     511    @Nullable
     512    public String getApertureValueDescription()
     513    {
     514        Double aperture = _directory.getDoubleObject(ExifSubIFDDirectory.TAG_APERTURE);
     515        if (aperture==null)
     516            return null;
     517        double fStop = PhotographicConversions.apertureToFStop(aperture);
     518        return "F" + SimpleDecimalFormatter.format(fStop);
     519    }
     520
     521    @Nullable
     522    public String getExposureProgramDescription()
     523    {
     524        // '1' means manual control, '2' program normal, '3' aperture priority,
     525        // '4' shutter priority, '5' program creative (slow program),
     526        // '6' program action(high-speed program), '7' portrait mode, '8' landscape mode.
     527        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXPOSURE_PROGRAM);
     528        if (value==null)
     529            return null;
     530        switch (value) {
     531            case 1: return "Manual control";
     532            case 2: return "Program normal";
     533            case 3: return "Aperture priority";
     534            case 4: return "Shutter priority";
     535            case 5: return "Program creative (slow program)";
     536            case 6: return "Program action (high-speed program)";
     537            case 7: return "Portrait mode";
     538            case 8: return "Landscape mode";
     539            default:
     540                return "Unknown program (" + value + ")";
     541        }
     542    }
     543
     544    @Nullable
     545    public String getYCbCrSubsamplingDescription()
     546    {
     547        int[] positions = _directory.getIntArray(ExifSubIFDDirectory.TAG_YCBCR_SUBSAMPLING);
     548        if (positions==null)
     549            return null;
     550        if (positions[0] == 2 && positions[1] == 1) {
     551            return "YCbCr4:2:2";
     552        } else if (positions[0] == 2 && positions[1] == 2) {
     553            return "YCbCr4:2:0";
     554        } else {
     555            return "(Unknown)";
     556        }
     557    }
     558
     559    @Nullable
     560    public String getPlanarConfigurationDescription()
     561    {
     562        // When image format is no compression YCbCr, this value shows byte aligns of YCbCr
     563        // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling
     564        // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr
     565        // plane format.
     566        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_PLANAR_CONFIGURATION);
     567        if (value==null)
     568            return null;
     569        switch (value) {
     570            case 1: return "Chunky (contiguous for each subsampling pixel)";
     571            case 2: return "Separate (Y-plane/Cb-plane/Cr-plane format)";
     572            default:
     573                return "Unknown configuration";
     574        }
     575    }
     576
     577    @Nullable
     578    public String getSamplesPerPixelDescription()
     579    {
     580        String value = _directory.getString(ExifSubIFDDirectory.TAG_SAMPLES_PER_PIXEL);
     581        return value==null ? null : value + " samples/pixel";
     582    }
     583
     584    @Nullable
     585    public String getRowsPerStripDescription()
     586    {
     587        final String value = _directory.getString(ExifSubIFDDirectory.TAG_ROWS_PER_STRIP);
     588        return value==null ? null : value + " rows/strip";
     589    }
     590
     591    @Nullable
     592    public String getStripByteCountsDescription()
     593    {
     594        final String value = _directory.getString(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);
     595        return value==null ? null : value + " bytes";
     596    }
     597
     598    @Nullable
     599    public String getPhotometricInterpretationDescription()
     600    {
     601        // Shows the color space of the image data components
     602        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION);
     603        if (value==null)
     604            return null;
     605        switch (value) {
     606            case 0: return "WhiteIsZero";
     607            case 1: return "BlackIsZero";
     608            case 2: return "RGB";
     609            case 3: return "RGB Palette";
     610            case 4: return "Transparency Mask";
     611            case 5: return "CMYK";
     612            case 6: return "YCbCr";
     613            case 8: return "CIELab";
     614            case 9: return "ICCLab";
     615            case 10: return "ITULab";
     616            case 32803: return "Color Filter Array";
     617            case 32844: return "Pixar LogL";
     618            case 32845: return "Pixar LogLuv";
     619            case 32892: return "Linear Raw";
     620            default:
     621                return "Unknown colour space";
     622        }
     623    }
     624
     625    @Nullable
     626    public String getBitsPerSampleDescription()
     627    {
     628        String value = _directory.getString(ExifSubIFDDirectory.TAG_BITS_PER_SAMPLE);
     629        return value==null ? null : value + " bits/component/pixel";
     630    }
     631
     632    @Nullable
     633    public String getFocalPlaneXResolutionDescription()
     634    {
     635        Rational rational = _directory.getRational(ExifSubIFDDirectory.TAG_FOCAL_PLANE_X_RES);
     636        if (rational==null)
     637            return null;
     638        final String unit = getFocalPlaneResolutionUnitDescription();
     639        return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals)
     640            + (unit==null ? "" : " " + unit.toLowerCase());
     641    }
     642
     643    @Nullable
     644    public String getFocalPlaneYResolutionDescription()
     645    {
     646        Rational rational = _directory.getRational(ExifSubIFDDirectory.TAG_FOCAL_PLANE_Y_RES);
     647        if (rational==null)
     648            return null;
     649        final String unit = getFocalPlaneResolutionUnitDescription();
     650        return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals)
     651            + (unit==null ? "" : " " + unit.toLowerCase());
     652    }
     653
     654    @Nullable
     655    public String getFocalPlaneResolutionUnitDescription()
     656    {
     657        // Unit of FocalPlaneXResolution/FocalPlaneYResolution. '1' means no-unit,
     658        // '2' inch, '3' centimeter.
     659        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_FOCAL_PLANE_UNIT);
     660        if (value==null)
     661            return null;
     662        switch (value) {
     663            case 1: return "(No unit)";
     664            case 2: return "Inches";
     665            case 3: return "cm";
     666            default:
     667                return "";
     668        }
     669    }
     670
     671    @Nullable
     672    public String getExifImageWidthDescription()
     673    {
     674        final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH);
     675        if (value==null)
     676            return null;
     677        return value + " pixels";
     678    }
     679
     680    @Nullable
     681    public String getExifImageHeightDescription()
     682    {
     683        final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT);
     684        if (value==null)
     685            return null;
     686        return value + " pixels";
     687    }
     688
     689    @Nullable
     690    public String getColorSpaceDescription()
     691    {
     692        final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_COLOR_SPACE);
     693        if (value==null)
     694            return null;
     695        if (value == 1)
     696            return "sRGB";
     697        if (value == 65535)
     698            return "Undefined";
     699        return "Unknown";
     700    }
     701
     702    @Nullable
     703    public String getFocalLengthDescription()
     704    {
     705        Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_FOCAL_LENGTH);
     706        if (value==null)
     707            return null;
     708        java.text.DecimalFormat formatter = new DecimalFormat("0.0##");
     709        return formatter.format(value.doubleValue()) + " mm";
     710    }
     711
     712    @Nullable
     713    public String getFlashDescription()
     714    {
     715        /*
     716         * This is a bitmask.
     717         * 0 = flash fired
     718         * 1 = return detected
     719         * 2 = return able to be detected
     720         * 3 = unknown
     721         * 4 = auto used
     722         * 5 = unknown
     723         * 6 = red eye reduction used
     724         */
     725
     726        final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_FLASH);
     727
     728        if (value==null)
     729            return null;
     730
     731        StringBuilder sb = new StringBuilder();
     732
     733        if ((value & 0x1)!=0)
     734            sb.append("Flash fired");
     735        else
     736            sb.append("Flash did not fire");
     737
     738        // check if we're able to detect a return, before we mention it
     739        if ((value & 0x4)!=0)
     740        {
     741            if ((value & 0x2)!=0)
     742                sb.append(", return detected");
     743            else
     744                sb.append(", return not detected");
     745        }
     746
     747        if ((value & 0x10)!=0)
     748            sb.append(", auto");
     749
     750        if ((value & 0x40)!=0)
     751            sb.append(", red-eye reduction");
     752
     753        return sb.toString();
     754    }
     755
     756    @Nullable
     757    public String getWhiteBalanceDescription()
     758    {
     759        // '0' means unknown, '1' daylight, '2' fluorescent, '3' tungsten, '10' flash,
     760        // '17' standard light A, '18' standard light B, '19' standard light C, '20' D55,
     761        // '21' D65, '22' D75, '255' other.
     762        final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_WHITE_BALANCE);
     763        if (value==null)
     764            return null;
     765        switch (value) {
     766            case 0: return "Unknown";
     767            case 1: return "Daylight";
     768            case 2: return "Florescent";
     769            case 3: return "Tungsten";
     770            case 10: return "Flash";
     771            case 17: return "Standard light";
     772            case 18: return "Standard light (B)";
     773            case 19: return "Standard light (C)";
     774            case 20: return "D55";
     775            case 21: return "D65";
     776            case 22: return "D75";
     777            case 255: return "(Other)";
     778            default:
     779                return "Unknown (" + value + ")";
     780        }
     781    }
     782
     783    @Nullable
     784    public String getMeteringModeDescription()
     785    {
     786        // '0' means unknown, '1' average, '2' center weighted average, '3' spot
     787        // '4' multi-spot, '5' multi-segment, '6' partial, '255' other
     788        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_METERING_MODE);
     789        if (value==null)
     790            return null;
     791        switch (value) {
     792            case 0: return "Unknown";
     793            case 1: return "Average";
     794            case 2: return "Center weighted average";
     795            case 3: return "Spot";
     796            case 4: return "Multi-spot";
     797            case 5: return "Multi-segment";
     798            case 6: return "Partial";
     799            case 255: return "(Other)";
     800            default:
     801                return "";
     802        }
     803    }
     804
     805    @Nullable
     806    public String getSubjectDistanceDescription()
     807    {
     808        Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE);
     809        if (value==null)
     810            return null;
     811        java.text.DecimalFormat formatter = new DecimalFormat("0.0##");
     812        return formatter.format(value.doubleValue()) + " metres";
     813    }
     814
     815    @Nullable
     816    public String getCompressedAverageBitsPerPixelDescription()
     817    {
     818        Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL);
     819        if (value==null)
     820            return null;
     821        String ratio = value.toSimpleString(_allowDecimalRepresentationOfRationals);
     822        if (value.isInteger() && value.intValue() == 1) {
     823            return ratio + " bit/pixel";
     824        } else {
     825            return ratio + " bits/pixel";
     826        }
     827    }
     828
     829    @Nullable
     830    public String getExposureTimeDescription()
     831    {
     832        String value = _directory.getString(ExifSubIFDDirectory.TAG_EXPOSURE_TIME);
     833        return value==null ? null : value + " sec";
     834    }
     835
     836    @Nullable
     837    public String getShutterSpeedDescription()
     838    {
     839        // I believe this method to now be stable, but am leaving some alternative snippets of
     840        // code in here, to assist anyone who's looking into this (given that I don't have a public CVS).
     841
     842//        float apexValue = _directory.getFloat(ExifSubIFDDirectory.TAG_SHUTTER_SPEED);
     843//        int apexPower = (int)Math.pow(2.0, apexValue);
     844//        return "1/" + apexPower + " sec";
     845        // TODO test this method
     846        // thanks to Mark Edwards for spotting and patching a bug in the calculation of this
     847        // description (spotted bug using a Canon EOS 300D)
     848        // thanks also to Gli Blr for spotting this bug
     849        Float apexValue = _directory.getFloatObject(ExifSubIFDDirectory.TAG_SHUTTER_SPEED);
     850        if (apexValue==null)
     851            return null;
     852        if (apexValue<=1) {
     853            float apexPower = (float)(1/(Math.exp(apexValue*Math.log(2))));
     854            long apexPower10 = Math.round((double)apexPower * 10.0);
     855            float fApexPower = (float) apexPower10 / 10.0f;
     856            return fApexPower + " sec";
     857        } else {
     858            int apexPower = (int)((Math.exp(apexValue*Math.log(2))));
     859            return "1/" + apexPower + " sec";
     860        }
     861
     862/*
     863        // This alternative implementation offered by Bill Richards
     864        // TODO determine which is the correct / more-correct implementation
     865        double apexValue = _directory.getDouble(ExifSubIFDDirectory.TAG_SHUTTER_SPEED);
     866        double apexPower = Math.pow(2.0, apexValue);
     867
     868        StringBuffer sb = new StringBuffer();
     869        if (apexPower > 1)
     870            apexPower = Math.floor(apexPower);
     871
     872        if (apexPower < 1) {
     873            sb.append((int)Math.round(1/apexPower));
     874        } else {
     875            sb.append("1/");
     876            sb.append((int)apexPower);
     877        }
     878        sb.append(" sec");
     879        return sb.toString();
     880*/
     881
     882    }
     883
     884    @Nullable
     885    public String getFNumberDescription()
     886    {
     887        Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_FNUMBER);
     888        if (value==null)
     889            return null;
     890        return "F" + SimpleDecimalFormatter.format(value.doubleValue());
     891    }
     892
     893    @Nullable
     894    public String getSensingMethodDescription()
     895    {
     896        // '1' Not defined, '2' One-chip color area sensor, '3' Two-chip color area sensor
     897        // '4' Three-chip color area sensor, '5' Color sequential area sensor
     898        // '7' Trilinear sensor '8' Color sequential linear sensor,  'Other' reserved
     899        Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SENSING_METHOD);
     900        if (value==null)
     901            return null;
     902        switch (value) {
     903            case 1: return "(Not defined)";
     904            case 2: return "One-chip color area sensor";
     905            case 3: return "Two-chip color area sensor";
     906            case 4: return "Three-chip color area sensor";
     907            case 5: return "Color sequential area sensor";
     908            case 7: return "Trilinear sensor";
     909            case 8: return "Color sequential linear sensor";
     910            default:
     911                return "";
     912        }
     913    }
     914
     915    @Nullable
     916    public String getComponentConfigurationDescription()
     917    {
     918        int[] components = _directory.getIntArray(ExifSubIFDDirectory.TAG_COMPONENTS_CONFIGURATION);
     919        if (components==null)
     920            return null;
     921        String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"};
     922        StringBuilder componentConfig = new StringBuilder();
     923        for (int i = 0; i < Math.min(4, components.length); i++) {
     924            int j = components[i];
     925            if (j > 0 && j < componentStrings.length) {
     926                componentConfig.append(componentStrings[j]);
     927            }
     928        }
     929        return componentConfig.toString();
     930    }
     931}
  • src/com/drew/metadata/exif/ExifSubIFDDescriptor.java

  • src/com/drew/metadata/exif/ExifSubIFDDirectory.java

    Modification de propriétés sur src/com/drew/metadata/exif/ExifSubIFDDescriptor.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.metadata.Directory;
     25
     26import java.util.HashMap;
     27
     28/**
     29 * Describes Exif tags from the SubIFD directory.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     33public class ExifSubIFDDirectory extends Directory
     34{
     35    /**
     36     * The actual aperture value of lens when the image was taken. Unit is APEX.
     37     * To convert this value to ordinary F-number (F-stop), calculate this value's
     38     * power of root 2 (=1.4142). For example, if the ApertureValue is '5',
     39     * F-number is 1.4142^5 = F5.6.
     40     */
     41    public static final int TAG_APERTURE = 0x9202;
     42    /**
     43     * When image format is no compression, this value shows the number of bits
     44     * per component for each pixel. Usually this value is '8,8,8'.
     45     */
     46    public static final int TAG_BITS_PER_SAMPLE = 0x0102;
     47
     48    /**
     49     * Shows the color space of the image data components.
     50     * 0 = WhiteIsZero
     51     * 1 = BlackIsZero
     52     * 2 = RGB
     53     * 3 = RGB Palette
     54     * 4 = Transparency Mask
     55     * 5 = CMYK
     56     * 6 = YCbCr
     57     * 8 = CIELab
     58     * 9 = ICCLab
     59     * 10 = ITULab
     60     * 32803 = Color Filter Array
     61     * 32844 = Pixar LogL
     62     * 32845 = Pixar LogLuv
     63     * 34892 = Linear Raw
     64     */
     65    public static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106;
     66
     67    /**
     68     * 1 = No dithering or halftoning
     69     * 2 = Ordered dither or halftone
     70     * 3 = Randomized dither
     71     */
     72    public static final int TAG_THRESHOLDING = 0x0107;
     73
     74    /**
     75     * 1 = Normal
     76     * 2 = Reversed
     77     */
     78    public static final int TAG_FILL_ORDER = 0x010A;
     79    public static final int TAG_DOCUMENT_NAME = 0x010D;
     80
     81    /** The position in the file of raster data. */
     82    public static final int TAG_STRIP_OFFSETS = 0x0111;
     83    /** Each pixel is composed of this many samples. */
     84    public static final int TAG_SAMPLES_PER_PIXEL = 0x0115;
     85    /** The raster is codified by a single block of data holding this many rows. */
     86    public static final int TAG_ROWS_PER_STRIP = 0x116;
     87    /** The size of the raster data in bytes. */
     88    public static final int TAG_STRIP_BYTE_COUNTS = 0x0117;
     89    public static final int TAG_MIN_SAMPLE_VALUE = 0x0118;
     90    public static final int TAG_MAX_SAMPLE_VALUE = 0x0119;
     91    /**
     92     * When image format is no compression YCbCr, this value shows byte aligns of
     93     * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for
     94     * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and
     95     * stored to Y plane/Cb plane/Cr plane format.
     96     */
     97    public static final int TAG_PLANAR_CONFIGURATION = 0x011C;
     98    public static final int TAG_YCBCR_SUBSAMPLING = 0x0212;
     99
     100    /**
     101     * The new subfile type tag.
     102     * 0 = Full-resolution Image
     103     * 1 = Reduced-resolution image
     104     * 2 = Single page of multi-page image
     105     * 3 = Single page of multi-page reduced-resolution image
     106     * 4 = Transparency mask
     107     * 5 = Transparency mask of reduced-resolution image
     108     * 6 = Transparency mask of multi-page image
     109     * 7 = Transparency mask of reduced-resolution multi-page image
     110     */
     111    public static final int TAG_NEW_SUBFILE_TYPE = 0x00FE;
     112    /**
     113     * The old subfile type tag.
     114     * 1 = Full-resolution image (Main image)
     115     * 2 = Reduced-resolution image (Thumbnail)
     116     * 3 = Single page of multi-page image
     117     */
     118    public static final int TAG_SUBFILE_TYPE = 0x00FF;
     119    public static final int TAG_TRANSFER_FUNCTION = 0x012D;
     120    public static final int TAG_PREDICTOR = 0x013D;
     121    public static final int TAG_TILE_WIDTH = 0x0142;
     122    public static final int TAG_TILE_LENGTH = 0x0143;
     123    public static final int TAG_TILE_OFFSETS = 0x0144;
     124    public static final int TAG_TILE_BYTE_COUNTS = 0x0145;
     125    public static final int TAG_JPEG_TABLES = 0x015B;
     126    public static final int TAG_CFA_REPEAT_PATTERN_DIM = 0x828D;
     127    /** There are two definitions for CFA pattern, I don't know the difference... */
     128    public static final int TAG_CFA_PATTERN_2 = 0x828E;
     129    public static final int TAG_BATTERY_LEVEL = 0x828F;
     130    public static final int TAG_IPTC_NAA = 0x83BB;
     131    public static final int TAG_INTER_COLOR_PROFILE = 0x8773;
     132    public static final int TAG_SPECTRAL_SENSITIVITY = 0x8824;
     133    /**
     134     * Indicates the Opto-Electric Conversion Function (OECF) specified in ISO 14524.
     135     * <p/>
     136     * OECF is the relationship between the camera optical input and the image values.
     137     * <p/>
     138     * The values are:
     139     * <ul>
     140     *   <li>Two shorts, indicating respectively number of columns, and number of rows.</li>
     141     *   <li>For each column, the column name in a null-terminated ASCII string.</li>
     142     *   <li>For each cell, an SRATIONAL value.</li>
     143     * </ul>
     144     */
     145    public static final int TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION = 0x8828;
     146    public static final int TAG_INTERLACE = 0x8829;
     147    public static final int TAG_TIME_ZONE_OFFSET = 0x882A;
     148    public static final int TAG_SELF_TIMER_MODE = 0x882B;
     149    public static final int TAG_FLASH_ENERGY = 0x920B;
     150    public static final int TAG_SPATIAL_FREQ_RESPONSE = 0x920C;
     151    public static final int TAG_NOISE = 0x920D;
     152    public static final int TAG_IMAGE_NUMBER = 0x9211;
     153    public static final int TAG_SECURITY_CLASSIFICATION = 0x9212;
     154    public static final int TAG_IMAGE_HISTORY = 0x9213;
     155    public static final int TAG_SUBJECT_LOCATION = 0x9214;
     156    /** There are two definitions for exposure index, I don't know the difference... */
     157    public static final int TAG_EXPOSURE_INDEX_2 = 0x9215;
     158    public static final int TAG_TIFF_EP_STANDARD_ID = 0x9216;
     159    public static final int TAG_FLASH_ENERGY_2 = 0xA20B;
     160    public static final int TAG_SPATIAL_FREQ_RESPONSE_2 = 0xA20C;
     161    public static final int TAG_SUBJECT_LOCATION_2 = 0xA214;
     162    public static final int TAG_PAGE_NAME = 0x011D;
     163    /**
     164     * Exposure time (reciprocal of shutter speed). Unit is second.
     165     */
     166    public static final int TAG_EXPOSURE_TIME = 0x829A;
     167    /**
     168     * The actual F-number(F-stop) of lens when the image was taken.
     169     */
     170    public static final int TAG_FNUMBER = 0x829D;
     171    /**
     172     * Exposure program that the camera used when image was taken. '1' means
     173     * manual control, '2' program normal, '3' aperture priority, '4' shutter
     174     * priority, '5' program creative (slow program), '6' program action
     175     * (high-speed program), '7' portrait mode, '8' landscape mode.
     176     */
     177    public static final int TAG_EXPOSURE_PROGRAM = 0x8822;
     178    public static final int TAG_ISO_EQUIVALENT = 0x8827;
     179    public static final int TAG_EXIF_VERSION = 0x9000;
     180    public static final int TAG_DATETIME_ORIGINAL = 0x9003;
     181    public static final int TAG_DATETIME_DIGITIZED = 0x9004;
     182    public static final int TAG_COMPONENTS_CONFIGURATION = 0x9101;
     183    /**
     184     * Average (rough estimate) compression level in JPEG bits per pixel.
     185     * */
     186    public static final int TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL = 0x9102;
     187    /**
     188     * Shutter speed by APEX value. To convert this value to ordinary 'Shutter Speed';
     189     * calculate this value's power of 2, then reciprocal. For example, if the
     190     * ShutterSpeedValue is '4', shutter speed is 1/(24)=1/16 second.
     191     */
     192    public static final int TAG_SHUTTER_SPEED = 0x9201;
     193    public static final int TAG_BRIGHTNESS_VALUE = 0x9203;
     194    public static final int TAG_EXPOSURE_BIAS = 0x9204;
     195    /**
     196     * Maximum aperture value of lens. You can convert to F-number by calculating
     197     * power of root 2 (same process of ApertureValue:0x9202).
     198     * The actual aperture value of lens when the image was taken. To convert this
     199     * value to ordinary f-number(f-stop), calculate the value's power of root 2
     200     * (=1.4142). For example, if the ApertureValue is '5', f-number is 1.41425^5 = F5.6.
     201     */
     202    public static final int TAG_MAX_APERTURE = 0x9205;
     203    /**
     204     * Indicates the distance the autofocus camera is focused to.  Tends to be less accurate as distance increases.
     205     */
     206    public static final int TAG_SUBJECT_DISTANCE = 0x9206;
     207    /**
     208     * Exposure metering method. '0' means unknown, '1' average, '2' center
     209     * weighted average, '3' spot, '4' multi-spot, '5' multi-segment, '6' partial,
     210     * '255' other.
     211     */
     212    public static final int TAG_METERING_MODE = 0x9207;
     213
     214    public static final int TAG_LIGHT_SOURCE = 0x9208;
     215    /**
     216     * White balance (aka light source). '0' means unknown, '1' daylight,
     217     * '2' fluorescent, '3' tungsten, '10' flash, '17' standard light A,
     218     * '18' standard light B, '19' standard light C, '20' D55, '21' D65,
     219     * '22' D75, '255' other.
     220     */
     221    public static final int TAG_WHITE_BALANCE = 0x9208;
     222    /**
     223     * 0x0  = 0000000 = No Flash
     224     * 0x1  = 0000001 = Fired
     225     * 0x5  = 0000101 = Fired, Return not detected
     226     * 0x7  = 0000111 = Fired, Return detected
     227     * 0x9  = 0001001 = On
     228     * 0xd  = 0001101 = On, Return not detected
     229     * 0xf  = 0001111 = On, Return detected
     230     * 0x10 = 0010000 = Off
     231     * 0x18 = 0011000 = Auto, Did not fire
     232     * 0x19 = 0011001 = Auto, Fired
     233     * 0x1d = 0011101 = Auto, Fired, Return not detected
     234     * 0x1f = 0011111 = Auto, Fired, Return detected
     235     * 0x20 = 0100000 = No flash function
     236     * 0x41 = 1000001 = Fired, Red-eye reduction
     237     * 0x45 = 1000101 = Fired, Red-eye reduction, Return not detected
     238     * 0x47 = 1000111 = Fired, Red-eye reduction, Return detected
     239     * 0x49 = 1001001 = On, Red-eye reduction
     240     * 0x4d = 1001101 = On, Red-eye reduction, Return not detected
     241     * 0x4f = 1001111 = On, Red-eye reduction, Return detected
     242     * 0x59 = 1011001 = Auto, Fired, Red-eye reduction
     243     * 0x5d = 1011101 = Auto, Fired, Red-eye reduction, Return not detected
     244     * 0x5f = 1011111 = Auto, Fired, Red-eye reduction, Return detected
     245     *        6543210 (positions)
     246     *
     247     * This is a bitmask.
     248     * 0 = flash fired
     249     * 1 = return detected
     250     * 2 = return able to be detected
     251     * 3 = unknown
     252     * 4 = auto used
     253     * 5 = unknown
     254     * 6 = red eye reduction used
     255     */
     256    public static final int TAG_FLASH = 0x9209;
     257    /**
     258     * Focal length of lens used to take image.  Unit is millimeter.
     259     * Nice digital cameras actually save the focal length as a function of how far they are zoomed in.
     260     */
     261    public static final int TAG_FOCAL_LENGTH = 0x920A;
     262    public static final int TAG_USER_COMMENT = 0x9286;
     263    public static final int TAG_SUBSECOND_TIME = 0x9290;
     264    public static final int TAG_SUBSECOND_TIME_ORIGINAL = 0x9291;
     265    public static final int TAG_SUBSECOND_TIME_DIGITIZED = 0x9292;
     266    public static final int TAG_FLASHPIX_VERSION = 0xA000;
     267    /**
     268     * Defines Color Space. DCF image must use sRGB color space so value is
     269     * always '1'. If the picture uses the other color space, value is
     270     * '65535':Uncalibrated.
     271     */
     272    public static final int TAG_COLOR_SPACE = 0xA001;
     273    public static final int TAG_EXIF_IMAGE_WIDTH = 0xA002;
     274    public static final int TAG_EXIF_IMAGE_HEIGHT = 0xA003;
     275    public static final int TAG_RELATED_SOUND_FILE = 0xA004;
     276    public static final int TAG_FOCAL_PLANE_X_RES = 0xA20E;
     277    public static final int TAG_FOCAL_PLANE_Y_RES = 0xA20F;
     278    /**
     279     * Unit of FocalPlaneXResolution/FocalPlaneYResolution. '1' means no-unit,
     280     * '2' inch, '3' centimeter.
     281     *
     282     * Note: Some of Fujifilm's digicam(e.g.FX2700,FX2900,Finepix4700Z/40i etc)
     283     * uses value '3' so it must be 'centimeter', but it seems that they use a
     284     * '8.3mm?'(1/3in.?) to their ResolutionUnit. Fuji's BUG? Finepix4900Z has
     285     * been changed to use value '2' but it doesn't match to actual value also.
     286     */
     287    public static final int TAG_FOCAL_PLANE_UNIT = 0xA210;
     288    public static final int TAG_EXPOSURE_INDEX = 0xA215;
     289    public static final int TAG_SENSING_METHOD = 0xA217;
     290    public static final int TAG_FILE_SOURCE = 0xA300;
     291    public static final int TAG_SCENE_TYPE = 0xA301;
     292    public static final int TAG_CFA_PATTERN = 0xA302;
     293
     294    // these tags new with Exif 2.2 (?) [A401 - A4
     295    /**
     296     * This tag indicates the use of special processing on image data, such as rendering
     297     * geared to output. When special processing is performed, the reader is expected to
     298     * disable or minimize any further processing.
     299     * Tag = 41985 (A401.H)
     300     * Type = SHORT
     301     * Count = 1
     302     * Default = 0
     303     *   0 = Normal process
     304     *   1 = Custom process
     305     *   Other = reserved
     306     */
     307    public static final int TAG_CUSTOM_RENDERED = 0xA401;
     308
     309    /**
     310     * This tag indicates the exposure mode set when the image was shot. In auto-bracketing
     311     * mode, the camera shoots a series of frames of the same scene at different exposure settings.
     312     * Tag = 41986 (A402.H)
     313     * Type = SHORT
     314     * Count = 1
     315     * Default = none
     316     *   0 = Auto exposure
     317     *   1 = Manual exposure
     318     *   2 = Auto bracket
     319     *   Other = reserved
     320     */
     321    public static final int TAG_EXPOSURE_MODE = 0xA402;
     322
     323    /**
     324     * This tag indicates the white balance mode set when the image was shot.
     325     * Tag = 41987 (A403.H)
     326     * Type = SHORT
     327     * Count = 1
     328     * Default = none
     329     *   0 = Auto white balance
     330     *   1 = Manual white balance
     331     *   Other = reserved
     332     */
     333    public static final int TAG_WHITE_BALANCE_MODE = 0xA403;
     334
     335    /**
     336     * This tag indicates the digital zoom ratio when the image was shot. If the
     337     * numerator of the recorded value is 0, this indicates that digital zoom was
     338     * not used.
     339     * Tag = 41988 (A404.H)
     340     * Type = RATIONAL
     341     * Count = 1
     342     * Default = none
     343     */
     344    public static final int TAG_DIGITAL_ZOOM_RATIO = 0xA404;
     345
     346    /**
     347     * This tag indicates the equivalent focal length assuming a 35mm film camera,
     348     * in mm. A value of 0 means the focal length is unknown. Note that this tag
     349     * differs from the FocalLength tag.
     350     * Tag = 41989 (A405.H)
     351     * Type = SHORT
     352     * Count = 1
     353     * Default = none
     354     */
     355    public static final int TAG_35MM_FILM_EQUIV_FOCAL_LENGTH = 0xA405;
     356
     357    /**
     358     * This tag indicates the type of scene that was shot. It can also be used to
     359     * record the mode in which the image was shot. Note that this differs from
     360     * the scene type (SceneType) tag.
     361     * Tag = 41990 (A406.H)
     362     * Type = SHORT
     363     * Count = 1
     364     * Default = 0
     365     *   0 = Standard
     366     *   1 = Landscape
     367     *   2 = Portrait
     368     *   3 = Night scene
     369     *   Other = reserved
     370     */
     371    public static final int TAG_SCENE_CAPTURE_TYPE = 0xA406;
     372
     373    /**
     374     * This tag indicates the degree of overall image gain adjustment.
     375     * Tag = 41991 (A407.H)
     376     * Type = SHORT
     377     * Count = 1
     378     * Default = none
     379     *   0 = None
     380     *   1 = Low gain up
     381     *   2 = High gain up
     382     *   3 = Low gain down
     383     *   4 = High gain down
     384     *   Other = reserved
     385     */
     386    public static final int TAG_GAIN_CONTROL = 0xA407;
     387
     388    /**
     389     * This tag indicates the direction of contrast processing applied by the camera
     390     * when the image was shot.
     391     * Tag = 41992 (A408.H)
     392     * Type = SHORT
     393     * Count = 1
     394     * Default = 0
     395     *   0 = Normal
     396     *   1 = Soft
     397     *   2 = Hard
     398     *   Other = reserved
     399     */
     400    public static final int TAG_CONTRAST = 0xA408;
     401
     402    /**
     403     * This tag indicates the direction of saturation processing applied by the camera
     404     * when the image was shot.
     405     * Tag = 41993 (A409.H)
     406     * Type = SHORT
     407     * Count = 1
     408     * Default = 0
     409     *   0 = Normal
     410     *   1 = Low saturation
     411     *   2 = High saturation
     412     *   Other = reserved
     413     */
     414    public static final int TAG_SATURATION = 0xA409;
     415
     416    /**
     417     * This tag indicates the direction of sharpness processing applied by the camera
     418     * when the image was shot.
     419     * Tag = 41994 (A40A.H)
     420     * Type = SHORT
     421     * Count = 1
     422     * Default = 0
     423     *   0 = Normal
     424     *   1 = Soft
     425     *   2 = Hard
     426     *   Other = reserved
     427     */
     428    public static final int TAG_SHARPNESS = 0xA40A;
     429
     430    // TODO support this tag (I haven't seen a camera's actual implementation of this yet)
     431
     432    /**
     433     * This tag indicates information on the picture-taking conditions of a particular
     434     * camera model. The tag is used only to indicate the picture-taking conditions in
     435     * the reader.
     436     * Tag = 41995 (A40B.H)
     437     * Type = UNDEFINED
     438     * Count = Any
     439     * Default = none
     440     *
     441     * The information is recorded in the format shown below. The data is recorded
     442     * in Unicode using SHORT type for the number of display rows and columns and
     443     * UNDEFINED type for the camera settings. The Unicode (UCS-2) string including
     444     * Signature is NULL terminated. The specifics of the Unicode string are as given
     445     * in ISO/IEC 10464-1.
     446     *
     447     *      Length  Type        Meaning
     448     *      ------+-----------+------------------
     449     *      2       SHORT       Display columns
     450     *      2       SHORT       Display rows
     451     *      Any     UNDEFINED   Camera setting-1
     452     *      Any     UNDEFINED   Camera setting-2
     453     *      :       :           :
     454     *      Any     UNDEFINED   Camera setting-n
     455     */
     456    public static final int TAG_DEVICE_SETTING_DESCRIPTION = 0xA40B;
     457
     458    /**
     459     * This tag indicates the distance to the subject.
     460     * Tag = 41996 (A40C.H)
     461     * Type = SHORT
     462     * Count = 1
     463     * Default = none
     464     *   0 = unknown
     465     *   1 = Macro
     466     *   2 = Close view
     467     *   3 = Distant view
     468     *   Other = reserved
     469     */
     470    public static final int TAG_SUBJECT_DISTANCE_RANGE = 0xA40C;
     471
     472    /**
     473     * This tag indicates an identifier assigned uniquely to each image. It is
     474     * recorded as an ASCII string equivalent to hexadecimal notation and 128-bit
     475     * fixed length.
     476     * Tag = 42016 (A420.H)
     477     * Type = ASCII
     478     * Count = 33
     479     * Default = none
     480     */
     481    public static final int TAG_IMAGE_UNIQUE_ID = 0xA420;
     482
     483    /** String. */
     484    public static final int TAG_CAMERA_OWNER_NAME = 0xA430;
     485    /** String. */
     486    public static final int TAG_BODY_SERIAL_NUMBER = 0xA431;
     487    /** An array of four Rational64u numbers giving focal and aperture ranges. */
     488    public static final int TAG_LENS_SPECIFICATION = 0xA432;
     489    /** String. */
     490    public static final int TAG_LENS_MAKE = 0xA433;
     491    /** String. */
     492    public static final int TAG_LENS_MODEL = 0xA434;
     493    /** String. */
     494    public static final int TAG_LENS_SERIAL_NUMBER = 0xA435;
     495    /** Rational64u. */
     496    public static final int TAG_GAMMA = 0xA500;
     497
     498    public static final int TAG_LENS = 0xFDEA;
     499
     500    @NotNull
     501    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     502
     503    static
     504    {
     505        _tagNameMap.put(TAG_FILL_ORDER, "Fill Order");
     506        _tagNameMap.put(TAG_DOCUMENT_NAME, "Document Name");
     507        // TODO why don't these tags have fields associated with them?
     508        _tagNameMap.put(0x1000, "Related Image File Format");
     509        _tagNameMap.put(0x1001, "Related Image Width");
     510        _tagNameMap.put(0x1002, "Related Image Length");
     511        _tagNameMap.put(0x0156, "Transfer Range");
     512        _tagNameMap.put(0x0200, "JPEG Proc");
     513        _tagNameMap.put(TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL, "Compressed Bits Per Pixel");
     514        _tagNameMap.put(0x927C, "Maker Note");
     515        _tagNameMap.put(0xA005, "Interoperability Offset");
     516
     517        _tagNameMap.put(TAG_NEW_SUBFILE_TYPE, "New Subfile Type");
     518        _tagNameMap.put(TAG_SUBFILE_TYPE, "Subfile Type");
     519        _tagNameMap.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample");
     520        _tagNameMap.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation");
     521        _tagNameMap.put(TAG_THRESHOLDING, "Thresholding");
     522        _tagNameMap.put(TAG_STRIP_OFFSETS, "Strip Offsets");
     523        _tagNameMap.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel");
     524        _tagNameMap.put(TAG_ROWS_PER_STRIP, "Rows Per Strip");
     525        _tagNameMap.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts");
     526        _tagNameMap.put(TAG_PAGE_NAME, "Page Name");
     527        _tagNameMap.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration");
     528        _tagNameMap.put(TAG_TRANSFER_FUNCTION, "Transfer Function");
     529        _tagNameMap.put(TAG_PREDICTOR, "Predictor");
     530        _tagNameMap.put(TAG_TILE_WIDTH, "Tile Width");
     531        _tagNameMap.put(TAG_TILE_LENGTH, "Tile Length");
     532        _tagNameMap.put(TAG_TILE_OFFSETS, "Tile Offsets");
     533        _tagNameMap.put(TAG_TILE_BYTE_COUNTS, "Tile Byte Counts");
     534        _tagNameMap.put(TAG_JPEG_TABLES, "JPEG Tables");
     535        _tagNameMap.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling");
     536        _tagNameMap.put(TAG_CFA_REPEAT_PATTERN_DIM, "CFA Repeat Pattern Dim");
     537        _tagNameMap.put(TAG_CFA_PATTERN_2, "CFA Pattern");
     538        _tagNameMap.put(TAG_BATTERY_LEVEL, "Battery Level");
     539        _tagNameMap.put(TAG_EXPOSURE_TIME, "Exposure Time");
     540        _tagNameMap.put(TAG_FNUMBER, "F-Number");
     541        _tagNameMap.put(TAG_IPTC_NAA, "IPTC/NAA");
     542        _tagNameMap.put(TAG_INTER_COLOR_PROFILE, "Inter Color Profile");
     543        _tagNameMap.put(TAG_EXPOSURE_PROGRAM, "Exposure Program");
     544        _tagNameMap.put(TAG_SPECTRAL_SENSITIVITY, "Spectral Sensitivity");
     545        _tagNameMap.put(TAG_ISO_EQUIVALENT, "ISO Speed Ratings");
     546        _tagNameMap.put(TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION, "Opto-electric Conversion Function (OECF)");
     547        _tagNameMap.put(TAG_INTERLACE, "Interlace");
     548        _tagNameMap.put(TAG_TIME_ZONE_OFFSET, "Time Zone Offset");
     549        _tagNameMap.put(TAG_SELF_TIMER_MODE, "Self Timer Mode");
     550        _tagNameMap.put(TAG_EXIF_VERSION, "Exif Version");
     551        _tagNameMap.put(TAG_DATETIME_ORIGINAL, "Date/Time Original");
     552        _tagNameMap.put(TAG_DATETIME_DIGITIZED, "Date/Time Digitized");
     553        _tagNameMap.put(TAG_COMPONENTS_CONFIGURATION, "Components Configuration");
     554        _tagNameMap.put(TAG_SHUTTER_SPEED, "Shutter Speed Value");
     555        _tagNameMap.put(TAG_APERTURE, "Aperture Value");
     556        _tagNameMap.put(TAG_BRIGHTNESS_VALUE, "Brightness Value");
     557        _tagNameMap.put(TAG_EXPOSURE_BIAS, "Exposure Bias Value");
     558        _tagNameMap.put(TAG_MAX_APERTURE, "Max Aperture Value");
     559        _tagNameMap.put(TAG_SUBJECT_DISTANCE, "Subject Distance");
     560        _tagNameMap.put(TAG_METERING_MODE, "Metering Mode");
     561        _tagNameMap.put(TAG_LIGHT_SOURCE, "Light Source");
     562        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
     563        _tagNameMap.put(TAG_FLASH, "Flash");
     564        _tagNameMap.put(TAG_FOCAL_LENGTH, "Focal Length");
     565        _tagNameMap.put(TAG_FLASH_ENERGY, "Flash Energy");
     566        _tagNameMap.put(TAG_SPATIAL_FREQ_RESPONSE, "Spatial Frequency Response");
     567        _tagNameMap.put(TAG_NOISE, "Noise");
     568        _tagNameMap.put(TAG_IMAGE_NUMBER, "Image Number");
     569        _tagNameMap.put(TAG_SECURITY_CLASSIFICATION, "Security Classification");
     570        _tagNameMap.put(TAG_IMAGE_HISTORY, "Image History");
     571        _tagNameMap.put(TAG_SUBJECT_LOCATION, "Subject Location");
     572        _tagNameMap.put(TAG_EXPOSURE_INDEX, "Exposure Index");
     573        _tagNameMap.put(TAG_TIFF_EP_STANDARD_ID, "TIFF/EP Standard ID");
     574        _tagNameMap.put(TAG_USER_COMMENT, "User Comment");
     575        _tagNameMap.put(TAG_SUBSECOND_TIME, "Sub-Sec Time");
     576        _tagNameMap.put(TAG_SUBSECOND_TIME_ORIGINAL, "Sub-Sec Time Original");
     577        _tagNameMap.put(TAG_SUBSECOND_TIME_DIGITIZED, "Sub-Sec Time Digitized");
     578        _tagNameMap.put(TAG_FLASHPIX_VERSION, "FlashPix Version");
     579        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
     580        _tagNameMap.put(TAG_EXIF_IMAGE_WIDTH, "Exif Image Width");
     581        _tagNameMap.put(TAG_EXIF_IMAGE_HEIGHT, "Exif Image Height");
     582        _tagNameMap.put(TAG_RELATED_SOUND_FILE, "Related Sound File");
     583        // 0x920B in TIFF/EP
     584        _tagNameMap.put(TAG_FLASH_ENERGY_2, "Flash Energy");
     585        // 0x920C in TIFF/EP
     586        _tagNameMap.put(TAG_SPATIAL_FREQ_RESPONSE_2, "Spatial Frequency Response");
     587        // 0x920E in TIFF/EP
     588        _tagNameMap.put(TAG_FOCAL_PLANE_X_RES, "Focal Plane X Resolution");
     589        // 0x920F in TIFF/EP
     590        _tagNameMap.put(TAG_FOCAL_PLANE_Y_RES, "Focal Plane Y Resolution");
     591        // 0x9210 in TIFF/EP
     592        _tagNameMap.put(TAG_FOCAL_PLANE_UNIT, "Focal Plane Resolution Unit");
     593        // 0x9214 in TIFF/EP
     594        _tagNameMap.put(TAG_SUBJECT_LOCATION_2, "Subject Location");
     595        // 0x9215 in TIFF/EP
     596        _tagNameMap.put(TAG_EXPOSURE_INDEX_2, "Exposure Index");
     597        // 0x9217 in TIFF/EP
     598        _tagNameMap.put(TAG_SENSING_METHOD, "Sensing Method");
     599        _tagNameMap.put(TAG_FILE_SOURCE, "File Source");
     600        _tagNameMap.put(TAG_SCENE_TYPE, "Scene Type");
     601        _tagNameMap.put(TAG_CFA_PATTERN, "CFA Pattern");
     602
     603        _tagNameMap.put(TAG_CUSTOM_RENDERED, "Custom Rendered");
     604        _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode");
     605        _tagNameMap.put(TAG_WHITE_BALANCE_MODE, "White Balance Mode");
     606        _tagNameMap.put(TAG_DIGITAL_ZOOM_RATIO, "Digital Zoom Ratio");
     607        _tagNameMap.put(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH, "Focal Length 35");
     608        _tagNameMap.put(TAG_SCENE_CAPTURE_TYPE, "Scene Capture Type");
     609        _tagNameMap.put(TAG_GAIN_CONTROL, "Gain Control");
     610        _tagNameMap.put(TAG_CONTRAST, "Contrast");
     611        _tagNameMap.put(TAG_SATURATION, "Saturation");
     612        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
     613        _tagNameMap.put(TAG_DEVICE_SETTING_DESCRIPTION, "Device Setting Description");
     614        _tagNameMap.put(TAG_SUBJECT_DISTANCE_RANGE, "Subject Distance Range");
     615        _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Unique Image ID");
     616       
     617        _tagNameMap.put(TAG_CAMERA_OWNER_NAME, "Camera Owner Name");
     618        _tagNameMap.put(TAG_BODY_SERIAL_NUMBER, "Body Serial Number");
     619        _tagNameMap.put(TAG_LENS_SPECIFICATION, "Lens Specification");
     620        _tagNameMap.put(TAG_LENS_MAKE, "Lens Make");
     621        _tagNameMap.put(TAG_LENS_MODEL, "Lens Model");
     622        _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
     623        _tagNameMap.put(TAG_GAMMA, "Gamma");
     624
     625        _tagNameMap.put(TAG_MIN_SAMPLE_VALUE, "Minimum sample value");
     626        _tagNameMap.put(TAG_MAX_SAMPLE_VALUE, "Maximum sample value");
     627
     628        _tagNameMap.put(TAG_LENS, "Lens");
     629    }
     630
     631    public ExifSubIFDDirectory()
     632    {
     633        this.setDescriptor(new ExifSubIFDDescriptor(this));
     634    }
     635
     636    @NotNull
     637    public String getName()
     638    {
     639        return "Exif SubIFD";
     640    }
     641
     642    @NotNull
     643    protected HashMap<Integer, String> getTagNameMap()
     644    {
     645        return _tagNameMap;
     646    }
     647}
  • src/com/drew/metadata/exif/ExifSubIFDDirectory.java

  • src/com/drew/metadata/exif/ExifThumbnailDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/exif/ExifSubIFDDirectory.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.Rational;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
     27import com.drew.metadata.TagDescriptor;
     28
     29/**
     30 * Provides human-readable string representations of tag values stored in a <code>ExifThumbnailDirectory</code>.
     31 *
     32 * @author Drew Noakes http://drewnoakes.com
     33 */
     34public class ExifThumbnailDescriptor extends TagDescriptor<ExifThumbnailDirectory>
     35{
     36    /**
     37     * Dictates whether rational values will be represented in decimal format in instances
     38     * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
     39     */
     40    private final boolean _allowDecimalRepresentationOfRationals = true;
     41
     42    public ExifThumbnailDescriptor(@NotNull ExifThumbnailDirectory directory)
     43    {
     44        super(directory);
     45    }
     46
     47    // Note for the potential addition of brightness presentation in eV:
     48    // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv),
     49    // you must add SensitivityValue(Sv).
     50    // Ev=BV+Sv   Sv=log2(ISOSpeedRating/3.125)
     51    // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
     52
     53    /**
     54     * Returns a descriptive value of the the specified tag for this image.
     55     * Where possible, known values will be substituted here in place of the raw
     56     * tokens actually kept in the Exif segment.  If no substitution is
     57     * available, the value provided by getString(int) will be returned.
     58     * @param tagType the tag to find a description for
     59     * @return a description of the image's value for the specified tag, or
     60     *         <code>null</code> if the tag hasn't been defined.
     61     */
     62    @Nullable
     63    public String getDescription(int tagType)
     64    {
     65        switch (tagType) {
     66            case ExifThumbnailDirectory.TAG_ORIENTATION:
     67                return getOrientationDescription();
     68            case ExifThumbnailDirectory.TAG_RESOLUTION_UNIT:
     69                return getResolutionDescription();
     70            case ExifThumbnailDirectory.TAG_YCBCR_POSITIONING:
     71                return getYCbCrPositioningDescription();
     72            case ExifThumbnailDirectory.TAG_X_RESOLUTION:
     73                return getXResolutionDescription();
     74            case ExifThumbnailDirectory.TAG_Y_RESOLUTION:
     75                return getYResolutionDescription();
     76            case ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET:
     77                return getThumbnailOffsetDescription();
     78            case ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH:
     79                return getThumbnailLengthDescription();
     80            case ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_WIDTH:
     81                return getThumbnailImageWidthDescription();
     82            case ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT:
     83                return getThumbnailImageHeightDescription();
     84            case ExifThumbnailDirectory.TAG_BITS_PER_SAMPLE:
     85                return getBitsPerSampleDescription();
     86            case ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION:
     87                return getCompressionDescription();
     88            case ExifThumbnailDirectory.TAG_PHOTOMETRIC_INTERPRETATION:
     89                return getPhotometricInterpretationDescription();
     90            case ExifThumbnailDirectory.TAG_ROWS_PER_STRIP:
     91                return getRowsPerStripDescription();
     92            case ExifThumbnailDirectory.TAG_STRIP_BYTE_COUNTS:
     93                return getStripByteCountsDescription();
     94            case ExifThumbnailDirectory.TAG_SAMPLES_PER_PIXEL:
     95                return getSamplesPerPixelDescription();
     96            case ExifThumbnailDirectory.TAG_PLANAR_CONFIGURATION:
     97                return getPlanarConfigurationDescription();
     98            case ExifThumbnailDirectory.TAG_YCBCR_SUBSAMPLING:
     99                return getYCbCrSubsamplingDescription();
     100            case ExifThumbnailDirectory.TAG_REFERENCE_BLACK_WHITE:
     101                return getReferenceBlackWhiteDescription();
     102            default:
     103                return super.getDescription(tagType);
     104        }
     105    }
     106
     107    @Nullable
     108    public String getReferenceBlackWhiteDescription()
     109    {
     110        int[] ints = _directory.getIntArray(ExifThumbnailDirectory.TAG_REFERENCE_BLACK_WHITE);
     111        if (ints==null)
     112            return null;
     113        int blackR = ints[0];
     114        int whiteR = ints[1];
     115        int blackG = ints[2];
     116        int whiteG = ints[3];
     117        int blackB = ints[4];
     118        int whiteB = ints[5];
     119        return "[" + blackR + "," + blackG + "," + blackB + "] " +
     120               "[" + whiteR + "," + whiteG + "," + whiteB + "]";
     121    }
     122
     123    @Nullable
     124    public String getYCbCrSubsamplingDescription()
     125    {
     126        int[] positions = _directory.getIntArray(ExifThumbnailDirectory.TAG_YCBCR_SUBSAMPLING);
     127        if (positions==null || positions.length < 2)
     128            return null;
     129        if (positions[0] == 2 && positions[1] == 1) {
     130            return "YCbCr4:2:2";
     131        } else if (positions[0] == 2 && positions[1] == 2) {
     132            return "YCbCr4:2:0";
     133        } else {
     134            return "(Unknown)";
     135        }
     136    }
     137
     138    @Nullable
     139    public String getPlanarConfigurationDescription()
     140    {
     141        // When image format is no compression YCbCr, this value shows byte aligns of YCbCr
     142        // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling
     143        // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr
     144        // plane format.
     145        Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_PLANAR_CONFIGURATION);
     146        if (value==null)
     147            return null;
     148        switch (value) {
     149            case 1: return "Chunky (contiguous for each subsampling pixel)";
     150            case 2: return "Separate (Y-plane/Cb-plane/Cr-plane format)";
     151            default:
     152                return "Unknown configuration";
     153        }
     154    }
     155
     156    @Nullable
     157    public String getSamplesPerPixelDescription()
     158    {
     159        String value = _directory.getString(ExifThumbnailDirectory.TAG_SAMPLES_PER_PIXEL);
     160        return value==null ? null : value + " samples/pixel";
     161    }
     162
     163    @Nullable
     164    public String getRowsPerStripDescription()
     165    {
     166        final String value = _directory.getString(ExifThumbnailDirectory.TAG_ROWS_PER_STRIP);
     167        return value==null ? null : value + " rows/strip";
     168    }
     169
     170    @Nullable
     171    public String getStripByteCountsDescription()
     172    {
     173        final String value = _directory.getString(ExifThumbnailDirectory.TAG_STRIP_BYTE_COUNTS);
     174        return value==null ? null : value + " bytes";
     175    }
     176
     177    @Nullable
     178    public String getPhotometricInterpretationDescription()
     179    {
     180        // Shows the color space of the image data components
     181        Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_PHOTOMETRIC_INTERPRETATION);
     182        if (value==null)
     183            return null;
     184        switch (value) {
     185            case 0: return "WhiteIsZero";
     186            case 1: return "BlackIsZero";
     187            case 2: return "RGB";
     188            case 3: return "RGB Palette";
     189            case 4: return "Transparency Mask";
     190            case 5: return "CMYK";
     191            case 6: return "YCbCr";
     192            case 8: return "CIELab";
     193            case 9: return "ICCLab";
     194            case 10: return "ITULab";
     195            case 32803: return "Color Filter Array";
     196            case 32844: return "Pixar LogL";
     197            case 32845: return "Pixar LogLuv";
     198            case 32892: return "Linear Raw";
     199            default:
     200                return "Unknown colour space";
     201        }
     202    }
     203
     204    @Nullable
     205    public String getCompressionDescription()
     206    {
     207        Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION);
     208        if (value==null)
     209            return null;
     210        switch (value) {
     211            case 1: return "Uncompressed";
     212            case 2: return "CCITT 1D";
     213            case 3: return "T4/Group 3 Fax";
     214            case 4: return "T6/Group 4 Fax";
     215            case 5: return "LZW";
     216            case 6: return "JPEG (old-style)";
     217            case 7: return "JPEG";
     218            case 8: return "Adobe Deflate";
     219            case 9: return "JBIG B&W";
     220            case 10: return "JBIG Color";
     221            case 32766: return "Next";
     222            case 32771: return "CCIRLEW";
     223            case 32773: return "PackBits";
     224            case 32809: return "Thunderscan";
     225            case 32895: return "IT8CTPAD";
     226            case 32896: return "IT8LW";
     227            case 32897: return "IT8MP";
     228            case 32898: return "IT8BL";
     229            case 32908: return "PixarFilm";
     230            case 32909: return "PixarLog";
     231            case 32946: return "Deflate";
     232            case 32947: return "DCS";
     233            case 32661: return "JBIG";
     234            case 32676: return "SGILog";
     235            case 32677: return "SGILog24";
     236            case 32712: return "JPEG 2000";
     237            case 32713: return "Nikon NEF Compressed";
     238            default:
     239                return "Unknown compression";
     240        }
     241    }
     242
     243    @Nullable
     244    public String getBitsPerSampleDescription()
     245    {
     246        String value = _directory.getString(ExifThumbnailDirectory.TAG_BITS_PER_SAMPLE);
     247        return value==null ? null : value + " bits/component/pixel";
     248    }
     249
     250    @Nullable
     251    public String getThumbnailImageWidthDescription()
     252    {
     253        String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
     254        return value==null ? null : value + " pixels";
     255    }
     256
     257    @Nullable
     258    public String getThumbnailImageHeightDescription()
     259    {
     260        String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
     261        return value==null ? null : value + " pixels";
     262    }
     263
     264    @Nullable
     265    public String getThumbnailLengthDescription()
     266    {
     267        String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH);
     268        return value==null ? null : value + " bytes";
     269    }
     270
     271    @Nullable
     272    public String getThumbnailOffsetDescription()
     273    {
     274        String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET);
     275        return value==null ? null : value + " bytes";
     276    }
     277
     278    @Nullable
     279    public String getYResolutionDescription()
     280    {
     281        Rational value = _directory.getRational(ExifThumbnailDirectory.TAG_Y_RESOLUTION);
     282        if (value==null)
     283            return null;
     284        final String unit = getResolutionDescription();
     285        return value.toSimpleString(_allowDecimalRepresentationOfRationals) +
     286                " dots per " +
     287                (unit==null ? "unit" : unit.toLowerCase());
     288    }
     289
     290    @Nullable
     291    public String getXResolutionDescription()
     292    {
     293        Rational value = _directory.getRational(ExifThumbnailDirectory.TAG_X_RESOLUTION);
     294        if (value==null)
     295            return null;
     296        final String unit = getResolutionDescription();
     297        return value.toSimpleString(_allowDecimalRepresentationOfRationals) +
     298                " dots per " +
     299                (unit==null ? "unit" : unit.toLowerCase());
     300    }
     301
     302    @Nullable
     303    public String getYCbCrPositioningDescription()
     304    {
     305        Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_YCBCR_POSITIONING);
     306        if (value==null)
     307            return null;
     308        switch (value) {
     309            case 1: return "Center of pixel array";
     310            case 2: return "Datum point";
     311            default:
     312                return String.valueOf(value);
     313        }
     314    }
     315
     316    @Nullable
     317    public String getOrientationDescription()
     318    {
     319        Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_ORIENTATION);
     320        if (value==null)
     321            return null;
     322        switch (value) {
     323            case 1: return "Top, left side (Horizontal / normal)";
     324            case 2: return "Top, right side (Mirror horizontal)";
     325            case 3: return "Bottom, right side (Rotate 180)";
     326            case 4: return "Bottom, left side (Mirror vertical)";
     327            case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)";
     328            case 6: return "Right side, top (Rotate 90 CW)";
     329            case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)";
     330            case 8: return "Left side, bottom (Rotate 270 CW)";
     331            default:
     332                return String.valueOf(value);
     333        }
     334    }
     335
     336    @Nullable
     337    public String getResolutionDescription()
     338    {
     339        // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
     340        Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_RESOLUTION_UNIT);
     341        if (value==null)
     342            return null;
     343        switch (value) {
     344            case 1: return "(No unit)";
     345            case 2: return "Inch";
     346            case 3: return "cm";
     347            default:
     348                return "";
     349        }
     350    }
     351}
  • src/com/drew/metadata/exif/ExifThumbnailDescriptor.java

  • src/com/drew/metadata/exif/ExifThumbnailDirectory.java

    Modification de propriétés sur src/com/drew/metadata/exif/ExifThumbnailDescriptor.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26import com.drew.metadata.Directory;
     27import com.drew.metadata.MetadataException;
     28
     29import java.io.FileOutputStream;
     30import java.io.IOException;
     31import java.util.HashMap;
     32
     33/**
     34 * One of several Exif directories.  Otherwise known as IFD1, this directory holds information about an embedded thumbnail image.
     35 *
     36 * @author Drew Noakes http://drewnoakes.com
     37 */
     38public class ExifThumbnailDirectory extends Directory
     39{
     40    public static final int TAG_THUMBNAIL_IMAGE_WIDTH = 0x0100;
     41    public static final int TAG_THUMBNAIL_IMAGE_HEIGHT = 0x0101;
     42
     43    /**
     44     * When image format is no compression, this value shows the number of bits
     45     * per component for each pixel. Usually this value is '8,8,8'.
     46     */
     47    public static final int TAG_BITS_PER_SAMPLE = 0x0102;
     48
     49    /**
     50     * Shows compression method for Thumbnail.
     51     * 1 = Uncompressed
     52     * 2 = CCITT 1D
     53     * 3 = T4/Group 3 Fax
     54     * 4 = T6/Group 4 Fax
     55     * 5 = LZW
     56     * 6 = JPEG (old-style)
     57     * 7 = JPEG
     58     * 8 = Adobe Deflate
     59     * 9 = JBIG B&W
     60     * 10 = JBIG Color
     61     * 32766 = Next
     62     * 32771 = CCIRLEW
     63     * 32773 = PackBits
     64     * 32809 = Thunderscan
     65     * 32895 = IT8CTPAD
     66     * 32896 = IT8LW
     67     * 32897 = IT8MP
     68     * 32898 = IT8BL
     69     * 32908 = PixarFilm
     70     * 32909 = PixarLog
     71     * 32946 = Deflate
     72     * 32947 = DCS
     73     * 34661 = JBIG
     74     * 34676 = SGILog
     75     * 34677 = SGILog24
     76     * 34712 = JPEG 2000
     77     * 34713 = Nikon NEF Compressed
     78     */
     79    public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103;
     80
     81    /**
     82     * Shows the color space of the image data components.
     83     * 0 = WhiteIsZero
     84     * 1 = BlackIsZero
     85     * 2 = RGB
     86     * 3 = RGB Palette
     87     * 4 = Transparency Mask
     88     * 5 = CMYK
     89     * 6 = YCbCr
     90     * 8 = CIELab
     91     * 9 = ICCLab
     92     * 10 = ITULab
     93     * 32803 = Color Filter Array
     94     * 32844 = Pixar LogL
     95     * 32845 = Pixar LogLuv
     96     * 34892 = Linear Raw
     97     */
     98    public static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106;
     99
     100    /** The position in the file of raster data. */
     101    public static final int TAG_STRIP_OFFSETS = 0x0111;
     102    public static final int TAG_ORIENTATION = 0x0112;
     103    /** Each pixel is composed of this many samples. */
     104    public static final int TAG_SAMPLES_PER_PIXEL = 0x0115;
     105    /** The raster is codified by a single block of data holding this many rows. */
     106    public static final int TAG_ROWS_PER_STRIP = 0x116;
     107    /** The size of the raster data in bytes. */
     108    public static final int TAG_STRIP_BYTE_COUNTS = 0x0117;
     109    /**
     110     * When image format is no compression YCbCr, this value shows byte aligns of
     111     * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for
     112     * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and
     113     * stored to Y plane/Cb plane/Cr plane format.
     114     */
     115    public static final int TAG_X_RESOLUTION = 0x011A;
     116    public static final int TAG_Y_RESOLUTION = 0x011B;
     117    public static final int TAG_PLANAR_CONFIGURATION = 0x011C;
     118    public static final int TAG_RESOLUTION_UNIT = 0x0128;
     119    /** The offset to thumbnail image bytes. */
     120    public static final int TAG_THUMBNAIL_OFFSET = 0x0201;
     121    /** The size of the thumbnail image data in bytes. */
     122    public static final int TAG_THUMBNAIL_LENGTH = 0x0202;
     123    public static final int TAG_YCBCR_COEFFICIENTS = 0x0211;
     124    public static final int TAG_YCBCR_SUBSAMPLING = 0x0212;
     125    public static final int TAG_YCBCR_POSITIONING = 0x0213;
     126    public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214;
     127
     128    @NotNull
     129    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     130
     131    static
     132    {
     133        _tagNameMap.put(TAG_THUMBNAIL_IMAGE_WIDTH, "Thumbnail Image Width");
     134        _tagNameMap.put(TAG_THUMBNAIL_IMAGE_HEIGHT, "Thumbnail Image Height");
     135        _tagNameMap.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample");
     136        _tagNameMap.put(TAG_THUMBNAIL_COMPRESSION, "Thumbnail Compression");
     137        _tagNameMap.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation");
     138        _tagNameMap.put(TAG_STRIP_OFFSETS, "Strip Offsets");
     139        _tagNameMap.put(TAG_ORIENTATION, "Orientation");
     140        _tagNameMap.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel");
     141        _tagNameMap.put(TAG_ROWS_PER_STRIP, "Rows Per Strip");
     142        _tagNameMap.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts");
     143        _tagNameMap.put(TAG_X_RESOLUTION, "X Resolution");
     144        _tagNameMap.put(TAG_Y_RESOLUTION, "Y Resolution");
     145        _tagNameMap.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration");
     146        _tagNameMap.put(TAG_RESOLUTION_UNIT, "Resolution Unit");
     147        _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset");
     148        _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length");
     149        _tagNameMap.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients");
     150        _tagNameMap.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling");
     151        _tagNameMap.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning");
     152        _tagNameMap.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White");
     153    }
     154
     155    @Nullable
     156    private byte[] _thumbnailData;
     157
     158    public ExifThumbnailDirectory()
     159    {
     160        this.setDescriptor(new ExifThumbnailDescriptor(this));
     161    }
     162
     163    @NotNull
     164    public String getName()
     165    {
     166        return "Exif Thumbnail";
     167    }
     168
     169    @NotNull
     170    protected HashMap<Integer, String> getTagNameMap()
     171    {
     172        return _tagNameMap;
     173    }
     174
     175    public boolean hasThumbnailData()
     176    {
     177        return _thumbnailData != null;
     178    }
     179
     180    @Nullable
     181    public byte[] getThumbnailData()
     182    {
     183        return _thumbnailData;
     184    }
     185
     186    public void setThumbnailData(@Nullable byte[] data)
     187    {
     188        _thumbnailData = data;
     189    }
     190
     191    public void writeThumbnail(@NotNull String filename) throws MetadataException, IOException
     192    {
     193        byte[] data = _thumbnailData;
     194
     195        if (data==null)
     196            throw new MetadataException("No thumbnail data exists.");
     197
     198        FileOutputStream stream = null;
     199        try {
     200            stream = new FileOutputStream(filename);
     201            stream.write(data);
     202        } finally {
     203            if (stream!=null)
     204                stream.close();
     205        }
     206    }
     207
     208/*
     209    // This thumbnail extraction code is not complete, and is included to assist anyone who feels like looking into
     210    // it.  Please share any progress with the original author, and hence the community.  Thanks.
     211
     212    public Image getThumbnailImage() throws MetadataException
     213    {
     214        if (!hasThumbnailData())
     215            return null;
     216
     217        int compression = 0;
     218        try {
     219            compression = this.getInt(ExifSubIFDDirectory.TAG_COMPRESSION);
     220        } catch (Throwable e) {
     221            this.addError("Unable to determine thumbnail type " + e.getMessage());
     222        }
     223
     224        final byte[] thumbnailBytes = getThumbnailData();
     225
     226        if (compression == ExifSubIFDDirectory.COMPRESSION_JPEG)
     227        {
     228            // JPEG Thumbnail
     229            // operate directly on thumbnailBytes
     230            return decodeBytesAsImage(thumbnailBytes);
     231        }
     232        else if (compression == ExifSubIFDDirectory.COMPRESSION_NONE)
     233        {
     234            // uncompressed thumbnail (raw RGB data)
     235            if (!this.containsTag(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION))
     236                return null;
     237
     238            try
     239            {
     240                // If the image is RGB format, then convert it to a bitmap
     241                final int photometricInterpretation = this.getInt(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION);
     242                if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_RGB)
     243                {
     244                    // RGB
     245                    Image image = createImageFromRawRgb(thumbnailBytes);
     246                    return image;
     247                }
     248                else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_YCBCR)
     249                {
     250                    // YCbCr
     251                    Image image = createImageFromRawYCbCr(thumbnailBytes);
     252                    return image;
     253                }
     254                else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_MONOCHROME)
     255                {
     256                    // Monochrome
     257                    return null;
     258                }
     259            } catch (Throwable e) {
     260                this.addError("Unable to extract thumbnail: " + e.getMessage());
     261            }
     262        }
     263        return null;
     264    }
     265
     266    /**
     267     * Handle the YCbCr thumbnail encoding used by Ricoh RDC4200/4300, Fuji DS-7/300 and DX-5/7/9 cameras.
     268     *
     269     * At DX-5/7/9, YCbCrSubsampling(0x0212) has values of '2,1', PlanarConfiguration(0x011c) has a value '1'. So the
     270     * data align of this image is below.
     271     *
     272     * Y(0,0),Y(1,0),Cb(0,0),Cr(0,0), Y(2,0),Y(3,0),Cb(2,0),Cr(3.0), Y(4,0),Y(5,0),Cb(4,0),Cr(4,0). . . .
     273     *
     274     * The numbers in parenthesis are pixel coordinates. DX series' YCbCrCoefficients(0x0211) has values '0.299/0.587/0.114',
     275     * ReferenceBlackWhite(0x0214) has values '0,255,128,255,128,255'. Therefore to convert from Y/Cb/Cr to RGB is;
     276     *
     277     * B(0,0)=(Cb-128)*(2-0.114*2)+Y(0,0)
     278     * R(0,0)=(Cr-128)*(2-0.299*2)+Y(0,0)
     279     * G(0,0)=(Y(0,0)-0.114*B(0,0)-0.299*R(0,0))/0.587
     280     *
     281     * Horizontal subsampling is a value '2', so you can calculate B(1,0)/R(1,0)/G(1,0) by using the Y(1,0) and Cr(0,0)/Cb(0,0).
     282     * Repeat this conversion by value of ImageWidth(0x0100) and ImageLength(0x0101).
     283     *
     284     * @param thumbnailBytes
     285     * @return
     286     * @throws com.drew.metadata.MetadataException
     287     * /
     288    private Image createImageFromRawYCbCr(byte[] thumbnailBytes) throws MetadataException
     289    {
     290        /*
     291            Y  =  0.257R + 0.504G + 0.098B + 16
     292            Cb = -0.148R - 0.291G + 0.439B + 128
     293            Cr =  0.439R - 0.368G - 0.071B + 128
     294
     295            G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
     296            R = 1.164(Y-16) + 1.596(Cr-128)
     297            B = 1.164(Y-16) + 2.018(Cb-128)
     298
     299            R, G and B range from 0 to 255.
     300            Y ranges from 16 to 235.
     301            Cb and Cr range from 16 to 240.
     302
     303            http://www.faqs.org/faqs/graphics/colorspace-faq/
     304        * /
     305
     306        int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);
     307        final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
     308        final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
     309//        final int headerLength = 54;
     310//        byte[] result = new byte[length + headerLength];
     311//        // Add a windows BMP header described:
     312//        // http://www.onicos.com/staff/iz/formats/bmp.html
     313//        result[0] = 'B';
     314//        result[1] = 'M'; // File Type identifier
     315//        result[3] = (byte)(result.length / 256);
     316//        result[2] = (byte)result.length;
     317//        result[10] = (byte)headerLength;
     318//        result[14] = 40; // MS Windows BMP header
     319//        result[18] = (byte)imageWidth;
     320//        result[22] = (byte)imageHeight;
     321//        result[26] = 1;  // 1 Plane
     322//        result[28] = 24; // Colour depth
     323//        result[34] = (byte)length;
     324//        result[35] = (byte)(length / 256);
     325
     326        final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
     327
     328        // order is YCbCr and image is upside down, bitmaps are BGR
     329////        for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)
     330//        {
     331//            final int y =  thumbnailBytes[dataOffset - 2] & 0xFF;
     332//            final int cb = thumbnailBytes[dataOffset - 1] & 0xFF;
     333//            final int cr = thumbnailBytes[dataOffset] & 0xFF;
     334//            if (y<16 || y>235 || cb<16 || cb>240 || cr<16 || cr>240)
     335//                "".toString();
     336//
     337//            int g = (int)(1.164*(y-16) - 0.391*(cb-128) - 0.813*(cr-128));
     338//            int r = (int)(1.164*(y-16) + 1.596*(cr-128));
     339//            int b = (int)(1.164*(y-16) + 2.018*(cb-128));
     340//
     341////            result[i] = (byte)b;
     342////            result[i + 1] = (byte)g;
     343////            result[i + 2] = (byte)r;
     344//
     345//            // TODO compose the image here
     346//            image.setRGB(1, 2, 3);
     347//        }
     348
     349        return image;
     350    }
     351
     352    /**
     353     * Creates a thumbnail image in (Windows) BMP format from raw RGB data.
     354     * @param thumbnailBytes
     355     * @return
     356     * @throws com.drew.metadata.MetadataException
     357     * /
     358    private Image createImageFromRawRgb(byte[] thumbnailBytes) throws MetadataException
     359    {
     360        final int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);
     361        final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
     362        final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
     363//        final int headerLength = 54;
     364//        final byte[] result = new byte[length + headerLength];
     365//        // Add a windows BMP header described:
     366//        // http://www.onicos.com/staff/iz/formats/bmp.html
     367//        result[0] = 'B';
     368//        result[1] = 'M'; // File Type identifier
     369//        result[3] = (byte)(result.length / 256);
     370//        result[2] = (byte)result.length;
     371//        result[10] = (byte)headerLength;
     372//        result[14] = 40; // MS Windows BMP header
     373//        result[18] = (byte)imageWidth;
     374//        result[22] = (byte)imageHeight;
     375//        result[26] = 1;  // 1 Plane
     376//        result[28] = 24; // Colour depth
     377//        result[34] = (byte)length;
     378//        result[35] = (byte)(length / 256);
     379
     380        final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
     381
     382        // order is RGB and image is upside down, bitmaps are BGR
     383//        for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)
     384//        {
     385//            byte b = thumbnailBytes[dataOffset - 2];
     386//            byte g = thumbnailBytes[dataOffset - 1];
     387//            byte r = thumbnailBytes[dataOffset];
     388//
     389//            // TODO compose the image here
     390//            image.setRGB(1, 2, 3);
     391//        }
     392
     393        return image;
     394    }
     395*/
     396}
  • src/com/drew/metadata/exif/ExifThumbnailDirectory.java

  • src/com/drew/metadata/exif/FujifilmMakernoteDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/exif/ExifThumbnailDirectory.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
    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
    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
    3543 *   align.
    3644 * - The other manufacturer's MakerNote counts the "offset to data" from the first byte
    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) {
    5061            case FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS:
    5162                return getSharpnessDescription();
    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:
    5768                return getToneDescription();
     
    6374                return getMacroDescription();
    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:
    6980                return getPictureModeDescription();
     
    7687            case FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING:
    7788                return getAutoExposureWarningDescription();
    7889            default:
    79                 return _directory.getString(tagType);
     90                return super.getDescription(tagType);
    8091        }
    8192    }
    8293
    83     public String getAutoExposureWarningDescription() throws MetadataException
     94    @Nullable
     95    public String getSharpnessDescription()
    8496    {
    85         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING)) return null;
    86         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING);
     97        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS);
     98        if (value==null)
     99            return null;
    87100        switch (value) {
    88             case 0:
    89                 return "AE good";
    90101            case 1:
    91                 return "Over exposed (>1/1000s @ F11)";
     102                return "Softest";
     103            case 2:
     104                return "Soft";
     105            case 3:
     106                return "Normal";
     107            case 4:
     108                return "Hard";
     109            case 5:
     110                return "Hardest";
    92111            default:
    93112                return "Unknown (" + value + ")";
    94113        }
    95114    }
    96115
    97     public String getFocusWarningDescription() throws MetadataException
     116    @Nullable
     117    public String getWhiteBalanceDescription()
    98118    {
    99         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING)) return null;
    100         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING);
     119        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE);
     120        if (value==null)
     121            return null;
    101122        switch (value) {
    102123            case 0:
    103                 return "Auto focus good";
    104             case 1:
    105                 return "Out of focus";
     124                return "Auto";
     125            case 256:
     126                return "Daylight";
     127            case 512:
     128                return "Cloudy";
     129            case 768:
     130                return "DaylightColor-fluorescence";
     131            case 769:
     132                return "DaywhiteColor-fluorescence";
     133            case 770:
     134                return "White-fluorescence";
     135            case 1024:
     136                return "Incandescence";
     137            case 3840:
     138                return "Custom white balance";
    106139            default:
    107140                return "Unknown (" + value + ")";
    108141        }
    109142    }
    110143
    111     public String getBlurWarningDescription() throws MetadataException
     144    @Nullable
     145    public String getColorDescription()
    112146    {
    113         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING)) return null;
    114         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING);
     147        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION);
     148        if (value==null)
     149            return null;
    115150        switch (value) {
    116151            case 0:
    117                 return "No blur warning";
    118             case 1:
    119                 return "Blur warning";
     152                return "Normal (STD)";
     153            case 256:
     154                return "High (HARD)";
     155            case 512:
     156                return "Low (ORG)";
    120157            default:
    121158                return "Unknown (" + value + ")";
    122159        }
    123160    }
    124161
    125     public String getContinuousTakingOrAutoBrackettingDescription() throws MetadataException
     162    @Nullable
     163    public String getToneDescription()
    126164    {
    127         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING)) return null;
    128         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING);
     165        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE);
     166        if (value==null)
     167            return null;
    129168        switch (value) {
    130169            case 0:
    131                 return "Off";
    132             case 1:
    133                 return "On";
     170                return "Normal (STD)";
     171            case 256:
     172                return "High (HARD)";
     173            case 512:
     174                return "Low (ORG)";
    134175            default:
    135176                return "Unknown (" + value + ")";
    136177        }
    137178    }
    138179
    139     public String getPictureModeDescription() throws MetadataException
     180    @Nullable
     181    public String getFlashModeDescription()
    140182    {
    141         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE)) return null;
    142         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE);
     183        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE);
     184        if (value==null)
     185            return null;
    143186        switch (value) {
    144187            case 0:
    145188                return "Auto";
    146189            case 1:
    147                 return "Portrait scene";
     190                return "On";
    148191            case 2:
    149                 return "Landscape scene";
    150             case 4:
    151                 return "Sports scene";
    152             case 5:
    153                 return "Night scene";
    154             case 6:
    155                 return "Program AE";
    156             case 256:
    157                 return "Aperture priority AE";
    158             case 512:
    159                 return "Shutter priority AE";
    160             case 768:
    161                 return "Manual exposure";
     192                return "Off";
     193            case 3:
     194                return "Red-eye reduction";
    162195            default:
    163196                return "Unknown (" + value + ")";
    164197        }
    165198    }
    166199
    167     public String getSlowSyncDescription() throws MetadataException
     200    @Nullable
     201    public String getFlashStrengthDescription()
    168202    {
    169         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO)) return null;
    170         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO);
    171         switch (value) {
    172             case 0:
    173                 return "Off";
    174             case 1:
    175                 return "On";
    176             default:
    177                 return "Unknown (" + value + ")";
    178         }
     203        Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH);
     204        if (value==null)
     205            return null;
     206        return value.toSimpleString(false) + " EV (Apex)";
    179207    }
    180208
    181     public String getFocusModeDescription() throws MetadataException
     209    @Nullable
     210    public String getMacroDescription()
    182211    {
    183         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE)) return null;
    184         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE);
    185         switch (value) {
    186             case 0:
    187                 return "Auto focus";
    188             case 1:
    189                 return "Manual focus";
    190             default:
    191                 return "Unknown (" + value + ")";
    192         }
     212        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO);
    193213    }
    194214
    195     public String getMacroDescription() throws MetadataException
     215    @Nullable
     216    public String getFocusModeDescription()
    196217    {
    197         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO)) return null;
    198         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO);
     218        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE);
     219        if (value==null)
     220            return null;
    199221        switch (value) {
    200222            case 0:
    201                 return "Off";
     223                return "Auto focus";
    202224            case 1:
    203                 return "On";
     225                return "Manual focus";
    204226            default:
    205227                return "Unknown (" + value + ")";
    206228        }
    207229    }
    208230
    209     public String getFlashStrengthDescription() throws MetadataException
     231    @Nullable
     232    public String getSlowSyncDescription()
    210233    {
    211         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH)) return null;
    212         Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH);
    213         return value.toSimpleString(false) + " EV (Apex)";
     234        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH);
    214235    }
    215236
    216     public String getFlashModeDescription() throws MetadataException
     237    @Nullable
     238    public String getPictureModeDescription()
    217239    {
    218         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE)) return null;
    219         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE);
     240        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE);
     241        if (value==null)
     242            return null;
    220243        switch (value) {
    221244            case 0:
    222245                return "Auto";
    223246            case 1:
    224                 return "On";
     247                return "Portrait scene";
    225248            case 2:
    226                 return "Off";
    227             case 3:
    228                 return "Red-eye reduction";
     249                return "Landscape scene";
     250            case 4:
     251                return "Sports scene";
     252            case 5:
     253                return "Night scene";
     254            case 6:
     255                return "Program AE";
     256            case 256:
     257                return "Aperture priority AE";
     258            case 512:
     259                return "Shutter priority AE";
     260            case 768:
     261                return "Manual exposure";
    229262            default:
    230263                return "Unknown (" + value + ")";
    231264        }
    232265    }
    233266
    234     public String getToneDescription() throws MetadataException
     267    @Nullable
     268    public String getContinuousTakingOrAutoBrackettingDescription()
    235269    {
    236         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE)) return null;
    237         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE);
     270        return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING);
     271    }
     272
     273    @Nullable
     274    public String getBlurWarningDescription()
     275    {
     276        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING);
     277        if (value==null)
     278            return null;
    238279        switch (value) {
    239280            case 0:
    240                 return "Normal (STD)";
    241             case 256:
    242                 return "High (HARD)";
    243             case 512:
    244                 return "Low (ORG)";
     281                return "No blur warning";
     282            case 1:
     283                return "Blur warning";
    245284            default:
    246285                return "Unknown (" + value + ")";
    247286        }
    248287    }
    249288
    250     public String getColorDescription() throws MetadataException
     289    @Nullable
     290    public String getFocusWarningDescription()
    251291    {
    252         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR)) return null;
    253         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR);
     292        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING);
     293        if (value==null)
     294            return null;
    254295        switch (value) {
    255296            case 0:
    256                 return "Normal (STD)";
    257             case 256:
    258                 return "High";
    259             case 512:
    260                 return "Low (ORG)";
     297                return "Auto focus good";
     298            case 1:
     299                return "Out of focus";
    261300            default:
    262301                return "Unknown (" + value + ")";
    263302        }
    264303    }
    265304
    266     public String getWhiteBalanceDescription() throws MetadataException
     305    @Nullable
     306    public String getAutoExposureWarningDescription()
    267307    {
    268         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE)) return null;
    269         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE);
     308        final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING);
     309        if (value==null)
     310            return null;
    270311        switch (value) {
    271312            case 0:
    272                 return "Auto";
    273             case 256:
    274                 return "Daylight";
    275             case 512:
    276                 return "Cloudy";
    277             case 768:
    278                 return "DaylightColor-fluorescence";
    279             case 769:
    280                 return "DaywhiteColor-fluorescence";
    281             case 770:
    282                 return "White-fluorescence";
    283             case 1024:
    284                 return "Incandenscense";
    285             case 3840:
    286                 return "Custom white balance";
     313                return "AE good";
     314            case 1:
     315                return "Over exposed (>1/1000s @ F11)";
    287316            default:
    288317                return "Unknown (" + value + ")";
    289318        }
    290319    }
    291320
    292     public String getSharpnessDescription() throws MetadataException
     321
     322    @Nullable
     323    private String getOnOffDescription(final int tagType)
    293324    {
    294         if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS)) return null;
    295         int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS);
     325        final Integer value = _directory.getInteger(tagType);
     326        if (value==null)
     327            return null;
    296328        switch (value) {
     329            case 0:
     330                return "Off";
    297331            case 1:
    298                 return "Softest";
    299             case 2:
    300                 return "Soft";
    301             case 3:
    302                 return "Normal";
    303             case 4:
    304                 return "Hard";
    305             case 5:
    306                 return "Hardest";
     332                return "On";
    307333            default:
    308334                return "Unknown (" + value + ")";
    309335        }
  • src/com/drew/metadata/exif/FujifilmMakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    7179    public FujifilmMakernoteDirectory()
     
    7381        this.setDescriptor(new FujifilmMakernoteDescriptor(this));
    7482    }
    7583
     84    @NotNull
    7685    public String getName()
    7786    {
    7887        return "FujiFilm Makernote";
    7988    }
    8089
    81     protected HashMap getTagNameMap()
     90    @NotNull
     91    protected HashMap<Integer, String> getTagNameMap()
    8292    {
    83         return tagNameMap;
     93        return _tagNameMap;
    8494    }
    8595}
  • src/com/drew/metadata/exif/GpsDescriptor.java

     
    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: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;
    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/**
     32 * Provides human-readable string representations of tag values stored in a <code>GpsDirectory</code>.
    2533 *
     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();
    3951            case GpsDirectory.TAG_GPS_ALTITUDE_REF:
     
    5668                return getGpsDestinationReferenceDescription();
    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);
     80                return super.getDescription(tagType);
    6681        }
    6782    }
    6883
    69     public String getGpsLatitudeDescription() throws MetadataException
     84    @Nullable
     85    private String getGpsVersionIdDescription()
    7086    {
    71         if (!_directory.containsTag(GpsDirectory.TAG_GPS_LATITUDE)) return null;
    72         return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LATITUDE);
     87        return convertBytesToVersionString(_directory.getIntArray(GpsDirectory.TAG_GPS_VERSION_ID), 1);
    7388    }
    7489
    75     public String getGpsLongitudeDescription() throws MetadataException
     90    @Nullable
     91    public String getGpsLatitudeDescription()
    7692    {
    77         if (!_directory.containsTag(GpsDirectory.TAG_GPS_LONGITUDE)) return null;
    78         return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LONGITUDE);
     93        GeoLocation location = _directory.getGeoLocation();
     94
     95        if (location == null)
     96            return null;
     97
     98        return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude());
    7999    }
    80100
    81     public String getHoursMinutesSecondsDescription(int tagType) throws MetadataException
     101    @Nullable
     102    public String getGpsLongitudeDescription()
    82103    {
    83         Rational[] components = _directory.getRationalArray(tagType);
    84         // TODO create an HoursMinutesSecods class ??
    85         int deg = components[0].intValue();
    86         float min = components[1].floatValue();
    87         float sec = components[2].floatValue();
    88         // carry fractions of minutes into seconds -- thanks Colin Briton
    89         sec += (min % 1) * 60;
    90         return String.valueOf(deg) + "\"" + String.valueOf((int)min) + "'" + String.valueOf(sec);
     104        GeoLocation location = _directory.getGeoLocation();
     105
     106        if (location == null)
     107            return null;
     108
     109        return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude());
    91110    }
    92111
    93     public String getGpsTimeStampDescription() throws MetadataException
     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();
     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();
    106127    }
    107128
     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 + ")";
     143            return "Unknown (" + distanceRef + ")";
    120144        }
    121145    }
    122146
     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";
     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";
    128158    }
    129159
     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";
    136169        } else if ("M".equalsIgnoreCase(gpsDistRef)) {
     
    140173        }
    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";
    149185        } else if ("M".equalsIgnoreCase(gpsSpeedRef)) {
     
    155191        }
    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";
    164203        } else if ("3".equalsIgnoreCase(gpsSpeedMeasureMode)) {
     
    168207        }
    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 + ")";
    181223        }
    182224    }
    183225
    184     public String getGpsAltitudeRefDescription() throws MetadataException
     226    @Nullable
     227    public String getGpsAltitudeRefDescription()
    185228    {
    186         if (!_directory.containsTag(GpsDirectory.TAG_GPS_ALTITUDE_REF)) return null;
    187         int alititudeRef = _directory.getInt(GpsDirectory.TAG_GPS_ALTITUDE_REF);
    188         if (alititudeRef == 0) {
     229        Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_ALTITUDE_REF);
     230        if (value==null)
     231            return null;
     232        if (value == 0)
    189233            return "Sea level";
    190         } else {
    191             return "Unknown (" + alititudeRef + ")";
    192         }
     234        if (value == 1)
     235            return "Below sea level";
     236        return "Unknown (" + value + ")";
    193237    }
    194238
    195     public String getGpsAltitudeDescription() throws MetadataException
     239    @Nullable
     240    public String getGpsAltitudeDescription()
    196241    {
    197         if (!_directory.containsTag(GpsDirectory.TAG_GPS_ALTITUDE)) return null;
    198         String alititude = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE).toSimpleString(true);
    199         return alititude + " metres";
     242        final Rational value = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE);
     243        if (value==null)
     244            return null;
     245        return value.intValue() + " metres";
    200246    }
     247
     248    @Nullable
     249    public String getGpsDifferentialDescription()
     250    {
     251        final Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_DIFFERENTIAL);
     252        if (value==null)
     253            return null;
     254        if (value == 0)
     255            return "No Correction";
     256        if (value == 1)
     257            return "Differential Corrected";
     258        return "Unknown (" + value + ")";
     259    }
     260
     261    @Nullable
     262    public String getDegreesMinutesSecondsDescription()
     263    {
     264        GeoLocation location = _directory.getGeoLocation();
     265
     266        if (location == null)
     267            return null;
     268
     269        return location.toDMSString();
     270    }
    201271}
  • src/com/drew/metadata/exif/GpsDirectory.java

     
    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
    2129import java.util.HashMap;
    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
    2737{
     
    8090    /** Distance to destination GPSDestDistance 26 1A RATIONAL 1 */
    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;
    8498
     99    @NotNull
     100    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     101
    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
    116137    public GpsDirectory()
     
    118139        this.setDescriptor(new GpsDescriptor(this));
    119140    }
    120141
     142    @NotNull
    121143    public String getName()
    122144    {
    123145        return "GPS";
    124146    }
    125147
    126     protected HashMap getTagNameMap()
     148    @NotNull
     149    protected HashMap<Integer, String> getTagNameMap()
    127150    {
    128         return tagNameMap;
     151        return _tagNameMap;
    129152    }
     153
     154    /**
     155     * Parses various tags in an attempt to obtain a single object representing the latitude and longitude
     156     * at which this image was captured.
     157     *
     158     * @return The geographical location of this image, if possible, otherwise null
     159     */
     160    @Nullable
     161    public GeoLocation getGeoLocation()
     162    {
     163        Rational[] latitudes = getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
     164        Rational[] longitudes = getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
     165        String latitudeRef = getString(GpsDirectory.TAG_GPS_LATITUDE_REF);
     166        String longitudeRef = getString(GpsDirectory.TAG_GPS_LONGITUDE_REF);
     167
     168        // Make sure we have the required values
     169        if (latitudes == null || latitudes.length != 3)
     170            return null;
     171        if (longitudes == null || longitudes.length != 3)
     172            return null;
     173        if (latitudeRef == null || longitudeRef == null)
     174            return null;
     175
     176        Double lat = GeoLocation.degreesMinutesSecondsToDecimal(latitudes[0], latitudes[1], latitudes[2], latitudeRef.equalsIgnoreCase("S"));
     177        Double lon = GeoLocation.degreesMinutesSecondsToDecimal(longitudes[0], longitudes[1], longitudes[2], longitudeRef.equalsIgnoreCase("W"));
     178
     179        // This can return null, in cases where the conversion was not possible
     180        if (lat == null || lon == null)
     181            return null;
     182
     183        return new GeoLocation(lat, lon);
     184    }
    130185}
  • src/com/drew/metadata/exif/KodakMakernoteDescriptor.java

     
    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}
  • src/com/drew/metadata/exif/KodakMakernoteDirectory.java

     
    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
    1926import java.util.HashMap;
    2027
    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}
  • src/com/drew/metadata/exif/KyoceraMakernoteDescriptor.java

     
    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) {
    4150            case KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO:
     
    4352            case KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL:
    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    }
    6376}
  • src/com/drew/metadata/exif/KyoceraMakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    2734{
    2835    public static final int TAG_KYOCERA_PROPRIETARY_THUMBNAIL = 0x0001;
    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
    3947    public KyoceraMakernoteDirectory()
     
    4149        this.setDescriptor(new KyoceraMakernoteDescriptor(this));
    4250    }
    4351
     52    @NotNull
    4453    public String getName()
    4554    {
    4655        return "Kyocera/Contax Makernote";
    4756    }
    4857
    49     protected HashMap getTagNameMap()
     58    @NotNull
     59    protected HashMap<Integer, String> getTagNameMap()
    5060    {
    51         return tagNameMap;
     61        return _tagNameMap;
    5262    }
    5363}
  • src/com/drew/metadata/exif/NikonType1MakernoteDescriptor.java

     
    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
    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
    2936 * structure is shown below.
     
    3138 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
    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) {
    4555            case NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY:
     
    5969            case NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER:
    6070                return getConverterDescription();
    6171            default:
    62                 return _directory.getString(tagType);
     72                return super.getDescription(tagType);
    6373        }
    6474    }
    6575
    66     public String getConverterDescription() throws MetadataException
     76    @Nullable
     77    public String getConverterDescription()
    6778    {
    68         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER)) return null;
    69         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER);
     79        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER);
     80        if (value == null)
     81            return null;
    7082        switch (value) {
    7183            case 0:
    7284                return "None";
     
    7789        }
    7890    }
    7991
    80     public String getDigitalZoomDescription() throws MetadataException
     92    @Nullable
     93    public String getDigitalZoomDescription()
    8194    {
    82         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM)) return null;
    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";
    86100        }
    87101        return value.toSimpleString(true) + "x digital zoom";
    88102    }
    89103
    90     public String getFocusDescription() throws MetadataException
     104    @Nullable
     105    public String getFocusDescription()
    91106    {
    92         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS)) return null;
    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";
    96112        }
    97113        return value.toSimpleString(true);
    98114    }
    99115
    100     public String getWhiteBalanceDescription() throws MetadataException
     116    @Nullable
     117    public String getWhiteBalanceDescription()
    101118    {
    102         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE)) return null;
    103         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE);
     119        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE);
     120        if (value == null)
     121            return null;
    104122        switch (value) {
    105123            case 0:
    106124                return "Auto";
     
    109127            case 2:
    110128                return "Daylight";
    111129            case 3:
    112                 return "Incandescense";
     130                return "Incandescence";
    113131            case 4:
    114                 return "Flourescence";
     132                return "Florescence";
    115133            case 5:
    116134                return "Cloudy";
    117135            case 6:
     
    121139        }
    122140    }
    123141
    124     public String getCcdSensitivityDescription() throws MetadataException
     142    @Nullable
     143    public String getCcdSensitivityDescription()
    125144    {
    126         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY)) return null;
    127         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY);
     145        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY);
     146        if (value == null)
     147            return null;
    128148        switch (value) {
    129149            case 0:
    130150                return "ISO80";
     
    139159        }
    140160    }
    141161
    142     public String getImageAdjustmentDescription() throws MetadataException
     162    @Nullable
     163    public String getImageAdjustmentDescription()
    143164    {
    144         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT)) return null;
    145         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT);
     165        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT);
     166        if (value == null)
     167            return null;
    146168        switch (value) {
    147169            case 0:
    148170                return "Normal";
     
    159181        }
    160182    }
    161183
    162     public String getColorModeDescription() throws MetadataException
     184    @Nullable
     185    public String getColorModeDescription()
    163186    {
    164         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE)) return null;
    165         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE);
     187        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE);
     188        if (value == null)
     189            return null;
    166190        switch (value) {
    167191            case 1:
    168192                return "Color";
     
    173197        }
    174198    }
    175199
    176     public String getQualityDescription() throws MetadataException
     200    @Nullable
     201    public String getQualityDescription()
    177202    {
    178         if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY)) return null;
    179         int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY);
     203        Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY);
     204        if (value == null)
     205            return null;
    180206        switch (value) {
    181207            case 1:
    182208                return "VGA Basic";
  • src/com/drew/metadata/exif/NikonType1MakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    2732 * starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from
     
    3136 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
    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
    3643{
     
    4653    public static final int TAG_NIKON_TYPE1_CONVERTER = 0x000B;
    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
    6674    public NikonType1MakernoteDirectory()
     
    6876        this.setDescriptor(new NikonType1MakernoteDescriptor(this));
    6977    }
    7078
     79    @NotNull
    7180    public String getName()
    7281    {
    7382        return "Nikon Makernote";
    7483    }
    7584
    76     protected HashMap getTagNameMap()
     85    @NotNull
     86    protected HashMap<Integer, String> getTagNameMap()
    7787    {
    7888        return _tagNameMap;
    7989    }
  • src/com/drew/metadata/exif/NikonType2MakernoteDescriptor.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  */
    15 package com.drew.metadata.exif;
    16 
    17 import com.drew.lang.Rational;
    18 import com.drew.metadata.Directory;
    19 import com.drew.metadata.MetadataException;
    20 import com.drew.metadata.TagDescriptor;
    21 
    22 import java.text.DecimalFormat;
    23 
    24 /**
    25  * Provides human-readable string versions of the tags stored in a NikonType2MakernoteDirectory.
    26  * Type-2 applies to the E990 and D-series cameras such as the D1, D70 and D100.
    27  */
    28 public class NikonType2MakernoteDescriptor extends TagDescriptor
    29 {
    30     public NikonType2MakernoteDescriptor(Directory directory)
    31     {
    32         super(directory);
    33     }
    34 
    35     private NikonType2MakernoteDirectory getMakernoteDirectory()
    36     {
    37         return (NikonType2MakernoteDirectory)_directory;
    38     }
    39 
    40     public String getDescription(int tagType) throws MetadataException
    41     {
    42         switch (tagType)
    43         {
    44             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS:
    45                 return getLensDescription();
    46             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT:
    47                 return getHueAdjustmentDescription();
    48             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE:
    49                 return getColorModeDescription();
    50             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION:
    51                 return getAutoFlashCompensationDescription();
    52             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1:
    53                 return getIsoSettingDescription();
    54             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM:
    55                 return getDigitalZoomDescription();
    56             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION:
    57                 return getAutoFocusPositionDescription();
    58             case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION:
    59                 return getAutoFirmwareVersionDescription();
    60             default:
    61                 return _directory.getString(tagType);
    62         }
    63     }
    64 
    65     public String getAutoFocusPositionDescription() throws MetadataException
    66     {
    67         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION)) return null;
    68         int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION);
    69         if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) {
    70             return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION) + ")";
    71         }
    72         switch (values[1]) {
    73             case 0:
    74                 return "Centre";
    75             case 1:
    76                 return "Top";
    77             case 2:
    78                 return "Bottom";
    79             case 3:
    80                 return "Left";
    81             case 4:
    82                 return "Right";
    83             default:
    84                 return "Unknown (" + values[1] + ")";
    85         }
    86     }
    87 
    88     public String getDigitalZoomDescription() throws MetadataException
    89     {
    90         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM)) return null;
    91         Rational rational = _directory.getRational(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM);
    92         if (rational.intValue() == 1) {
    93             return "No digital zoom";
    94         }
    95         return rational.toSimpleString(true) + "x digital zoom";
    96     }
    97 
    98     public String getIsoSettingDescription() throws MetadataException
    99     {
    100         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1)) return null;
    101         int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1);
    102         if (values[0] != 0 || values[1] == 0) {
    103             return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1) + ")";
    104         }
    105         return "ISO " + values[1];
    106     }
    107 
    108     public String getAutoFlashCompensationDescription() throws MetadataException
    109     {
    110         Rational ev = getMakernoteDirectory().getAutoFlashCompensation();
    111 
    112         if (ev==null)
    113             return "Unknown";
    114 
    115         DecimalFormat decimalFormat = new DecimalFormat("0.##");
    116         return decimalFormat.format(ev.floatValue()) + " EV";
    117     }
    118 
    119     public String getLensDescription() throws MetadataException
    120     {
    121         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS))
    122             return null;
    123 
    124         Rational[] lensValues = _directory.getRationalArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
    125 
    126         if (lensValues.length!=4)
    127             return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
    128 
    129         StringBuffer description = new StringBuffer();
    130         description.append(lensValues[0].intValue());
    131         description.append('-');
    132         description.append(lensValues[1].intValue());
    133         description.append("mm f/");
    134         description.append(lensValues[2].floatValue());
    135         description.append('-');
    136         description.append(lensValues[3].floatValue());
    137 
    138         return description.toString();
    139     }
    140 
    141     public String getHueAdjustmentDescription()
    142     {
    143         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT))
    144             return null;
    145 
    146         return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT) + " degrees";
    147     }
    148 
    149     public String getColorModeDescription()
    150     {
    151         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE))
    152             return null;
    153 
    154         String raw = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE);
    155         if (raw.startsWith("MODE1"))
    156             return "Mode I (sRGB)";
    157 
    158         return raw;
    159     }
    160 
    161     public String getAutoFirmwareVersionDescription() throws MetadataException
    162     {
    163         if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION))
    164             return null;
    165 
    166         int[] ints = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION);
    167         return ExifDescriptor.convertBytesToVersionString(ints);
    168     }
    169 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.lang.Rational;
     24import com.drew.lang.StringUtil;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.lang.annotations.Nullable;
     27import com.drew.metadata.TagDescriptor;
     28
     29import java.text.DecimalFormat;
     30import java.util.ArrayList;
     31import java.util.Collection;
     32import java.util.Date;
     33
     34/**
     35 * Provides human-readable string representations of tag values stored in a <code>NikonType2MakernoteDirectory</code>.
     36 *
     37 * Type-2 applies to the E990 and D-series cameras such as the D1, D70 and D100.
     38 *
     39 * @author Drew Noakes http://drewnoakes.com
     40 */
     41public class NikonType2MakernoteDescriptor extends TagDescriptor<NikonType2MakernoteDirectory>
     42{
     43    public NikonType2MakernoteDescriptor(@NotNull NikonType2MakernoteDirectory directory)
     44    {
     45        super(directory);
     46    }
     47
     48    @Nullable
     49    public String getDescription(int tagType)
     50    {
     51        switch (tagType)
     52        {
     53            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT:
     54                return getProgramShiftDescription();
     55            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE:
     56                return getExposureDifferenceDescription();
     57            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS:
     58                return getLensDescription();
     59            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT:
     60                return getHueAdjustmentDescription();
     61            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE:
     62                return getColorModeDescription();
     63            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION:
     64                return getAutoFlashCompensationDescription();
     65            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION:
     66                return getFlashExposureCompensationDescription();
     67            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION:
     68                return getFlashBracketCompensationDescription();
     69            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_TUNING:
     70                return getExposureTuningDescription();
     71            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_STOPS:
     72                return getLensStopsDescription();
     73            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_COLOR_SPACE:
     74                return getColorSpaceDescription();
     75            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING:
     76                return getActiveDLightingDescription();
     77            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_VIGNETTE_CONTROL:
     78                return getVignetteControlDescription();
     79            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1:
     80                return getIsoSettingDescription();
     81            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM:
     82                return getDigitalZoomDescription();
     83            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED:
     84                return getFlashUsedDescription();
     85            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION:
     86                return getAutoFocusPositionDescription();
     87            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION:
     88                return getFirmwareVersionDescription();
     89            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_TYPE:
     90                return getLensTypeDescription();
     91            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_SHOOTING_MODE:
     92                return getShootingModeDescription();
     93            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_NEF_COMPRESSION:
     94                return getNEFCompressionDescription();
     95            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION:
     96                return getHighISONoiseReductionDescription();
     97            case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_POWER_UP_TIME:
     98                return getPowerUpTimeDescription();
     99            default:
     100                return super.getDescription(tagType);
     101        }
     102    }
     103
     104    @Nullable
     105    public String getPowerUpTimeDescription()
     106    {
     107        Long value = _directory.getLongObject(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_POWER_UP_TIME);
     108        if (value==null)
     109            return null; // TODO have observed a byte[8] here which is likely some kind of date (ticks as long?)
     110        return new Date(value).toString();
     111    }
     112
     113    @Nullable
     114    public String getHighISONoiseReductionDescription()
     115    {
     116        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION);
     117        if (value==null)
     118            return null;
     119        switch (value) {
     120            case 0: return "Off";
     121            case 1: return "Minimal";
     122            case 2: return "Low";
     123            case 4: return "Normal";
     124            case 6: return "High";
     125            default: return "Unknown (" + value + ")";
     126        }
     127    }
     128
     129    @Nullable
     130    public String getFlashUsedDescription()
     131    {
     132        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED);
     133        if (value==null)
     134            return null;
     135        switch (value) {
     136            case 0: return "Flash Not Used";
     137            case 1: return "Manual Flash";
     138            case 3: return "Flash Not Ready";
     139            case 7: return "External Flash";
     140            case 8: return "Fired, Commander Mode";
     141            case 9: return "Fired, TTL Mode";
     142            default: return "Unknown (" + value + ")";
     143        }
     144    }
     145
     146    @Nullable
     147    public String getNEFCompressionDescription()
     148    {
     149        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_NEF_COMPRESSION);
     150        if (value==null)
     151            return null;
     152        switch (value) {
     153            case 1: return "Lossy (Type 1)";
     154            case 3: return "Uncompressed";
     155            case 7: return "Lossless";
     156            case 8: return "Lossy (Type 2)";
     157            default: return "Unknown (" + value + ")";
     158        }
     159    }
     160
     161    @Nullable
     162    public String getShootingModeDescription()
     163    {
     164        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_SHOOTING_MODE);
     165        if (value==null)
     166            return null;
     167        Collection<String> bits = new ArrayList<String>();
     168
     169        if ((value&1)==1)
     170            bits.add("Continuous");
     171        else
     172            bits.add("Single Frame");
     173
     174        if ((value&2)==2)
     175            bits.add("Delay");
     176        // Don't know about 3
     177        if ((value&8)==8)
     178            bits.add("PC Control");
     179        if ((value&16)==16)
     180            bits.add("Exposure Bracketing");
     181        if ((value&32)==32)
     182            bits.add("Auto ISO");
     183        if ((value&64)==64)
     184            bits.add("White-Balance Bracketing");
     185        if ((value&128)==128)
     186            bits.add("IR Control");
     187
     188        return StringUtil.join(bits, ", ");
     189    }
     190
     191    @Nullable
     192    public String getLensTypeDescription()
     193    {
     194        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_TYPE);
     195        if (value==null)
     196            return null;
     197
     198        Collection<String> bits = new ArrayList<String>();
     199
     200        // TODO validate these values, as 14 is labelled as AF-C elsewhere but appears here as AF-D-G-VR
     201
     202        if ((value&1)==1)
     203            bits.add("MF");
     204        else
     205            bits.add("AF");
     206
     207        if ((value&2)==2)
     208            bits.add("D");
     209
     210        if ((value&4)==4)
     211            bits.add("G");
     212
     213        if ((value&8)==8)
     214            bits.add("VR");
     215
     216        return StringUtil.join(bits, ", ");
     217    }
     218
     219    @Nullable
     220    public String getColorSpaceDescription()
     221    {
     222        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_COLOR_SPACE);
     223        if (value==null)
     224            return null;
     225        switch (value) {
     226            case 1: return "sRGB";
     227            case 2: return "Adobe RGB";
     228            default: return "Unknown (" + value + ")";
     229        }
     230    }
     231
     232    @Nullable
     233    public String getActiveDLightingDescription()
     234    {
     235        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING);
     236        if (value==null)
     237            return null;
     238        switch (value) {
     239            case 0: return "Off";
     240            case 1: return "Light";
     241            case 3: return "Normal";
     242            case 5: return "High";
     243            case 7: return "Extra High";
     244            case 65535: return "Auto";
     245            default: return "Unknown (" + value + ")";
     246        }
     247    }
     248
     249    @Nullable
     250    public String getVignetteControlDescription()
     251    {
     252        Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_VIGNETTE_CONTROL);
     253        if (value==null)
     254            return null;
     255        switch (value) {
     256            case 0: return "Off";
     257            case 1: return "Low";
     258            case 3: return "Normal";
     259            case 5: return "High";
     260            default: return "Unknown (" + value + ")";
     261        }
     262    }
     263
     264    @Nullable
     265    public String getAutoFocusPositionDescription()
     266    {
     267        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION);
     268        if (values==null)
     269            return null;
     270        if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) {
     271            return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION) + ")";
     272        }
     273        switch (values[1]) {
     274            case 0:
     275                return "Centre";
     276            case 1:
     277                return "Top";
     278            case 2:
     279                return "Bottom";
     280            case 3:
     281                return "Left";
     282            case 4:
     283                return "Right";
     284            default:
     285                return "Unknown (" + values[1] + ")";
     286        }
     287    }
     288
     289    @Nullable
     290    public String getDigitalZoomDescription()
     291    {
     292        Rational value = _directory.getRational(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM);
     293        if (value==null)
     294            return null;
     295        return value.intValue() == 1
     296                ? "No digital zoom"
     297                : value.toSimpleString(true) + "x digital zoom";
     298    }
     299
     300    @Nullable
     301    public String getProgramShiftDescription()
     302    {
     303        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT);
     304        return getEVDescription(values);
     305    }
     306
     307    @Nullable
     308    public String getExposureDifferenceDescription()
     309    {
     310        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE);
     311        return getEVDescription(values);
     312    }
     313
     314    @NotNull
     315    public String getAutoFlashCompensationDescription()
     316    {
     317        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION);
     318        return getEVDescription(values);
     319    }
     320
     321    @NotNull
     322    public String getFlashExposureCompensationDescription()
     323    {
     324        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION);
     325        return getEVDescription(values);
     326    }
     327
     328    @NotNull
     329    public String getFlashBracketCompensationDescription()
     330    {
     331        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION);
     332        return getEVDescription(values);
     333    }
     334
     335    @NotNull
     336    public String getExposureTuningDescription()
     337    {
     338        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_TUNING);
     339        return getEVDescription(values);
     340    }
     341
     342    @NotNull
     343    public String getLensStopsDescription()
     344    {
     345        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_STOPS);
     346        return getEVDescription(values);
     347    }
     348
     349    @Nullable
     350    private static String getEVDescription(@Nullable int[] values)
     351    {
     352        if (values==null)
     353            return null;
     354        if (values.length<3 || values[2]==0)
     355            return null;
     356        final DecimalFormat decimalFormat = new DecimalFormat("0.##");
     357        double ev = values[0] * values[1] / (double)values[2];
     358        return decimalFormat.format(ev) + " EV";
     359    }
     360
     361    @Nullable
     362    public String getIsoSettingDescription()
     363    {
     364        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1);
     365        if (values==null)
     366            return null;
     367        if (values[0] != 0 || values[1] == 0)
     368            return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1) + ")";
     369        return "ISO " + values[1];
     370    }
     371
     372    @Nullable
     373    public String getLensDescription()
     374    {
     375        Rational[] values = _directory.getRationalArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
     376
     377        if (values==null)
     378            return null;
     379
     380        if (values.length<4)
     381            return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS);
     382
     383        StringBuilder description = new StringBuilder();
     384        description.append(values[0].intValue());
     385        description.append('-');
     386        description.append(values[1].intValue());
     387        description.append("mm f/");
     388        description.append(values[2].floatValue());
     389        description.append('-');
     390        description.append(values[3].floatValue());
     391
     392        return description.toString();
     393    }
     394
     395    @Nullable
     396    public String getHueAdjustmentDescription()
     397    {
     398        final String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT);
     399        if (value==null)
     400            return null;
     401        return value + " degrees";
     402    }
     403
     404    @Nullable
     405    public String getColorModeDescription()
     406    {
     407        String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE);
     408        if (value==null)
     409            return null;
     410        if (value.startsWith("MODE1"))
     411            return "Mode I (sRGB)";
     412        return value;
     413    }
     414
     415    @Nullable
     416    public String getFirmwareVersionDescription()
     417    {
     418        int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION);
     419        if (values==null)
     420            return null;
     421        return ExifSubIFDDescriptor.convertBytesToVersionString(values, 2);
     422    }
     423}
  • src/com/drew/metadata/exif/NikonType2MakernoteDirectory.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   metadata_extractor [at] drewnoakes [dot] com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 3-Oct-2002 10:10:47 using IntelliJ IDEA.
    16  */
    17 package com.drew.metadata.exif;
    18 
    19 import com.drew.lang.Rational;
    20 import com.drew.metadata.Directory;
    21 import com.drew.metadata.MetadataException;
    22 
    23 import java.util.HashMap;
    24 
    25 /**
    26  * Describes tags specific to Nikon (type 2) cameras.  Type-2 applies to the E990 and D-series cameras such as the E990, D1,
    27  * D70 and D100.
    28  *
    29  * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data.
    30  * http://www.timelesswanderings.net/equipment/D100/NEF.html
    31  *
    32  * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute.  Similarly
    33  * image hiding uses the 'hidden' attribute (observed on the D70).  Consequently, these values are not available here.
    34  *
    35  * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field.
    36  * New tags have subsequently been added since Fabrizio's observations.
    37  *
    38  * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD.  In
    39  * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also
    40  * provide this prefix to aid in software decoding).
    41  */
    42 public class NikonType2MakernoteDirectory extends Directory
    43 {
    44     /**
    45      * Values observed
    46      * - 0200 (D70)
    47      * - 0200 (D1X)
    48      */
    49     public static final int TAG_NIKON_TYPE2_FIRMWARE_VERSION = 0x0001;
    50 
    51     /**
    52      * Values observed
    53      * - 0 250
    54      * - 0 400
    55      */
    56     public static final int TAG_NIKON_TYPE2_ISO_1 = 0x0002;
    57 
    58     /**
    59      * Values observed
    60      * - COLOR (seen in the D1X)
    61      */
    62     public static final int TAG_NIKON_TYPE2_COLOR_MODE = 0x0003;
    63 
    64     /**
    65      * Values observed
    66      * - FILE
    67      * - RAW
    68      * - NORMAL
    69      * - FINE
    70      */
    71     public static final int TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT = 0x0004;
    72 
    73     /**
    74      * The white balance as set in the camera.
    75      *
    76      * Values observed
    77      * - AUTO
    78      * - SUNNY (D70)
    79      * - FLASH (D1X)
    80      * (presumably also SHADOW / INCANDESCENT / FLUORESCENT / CLOUDY)
    81      */
    82     public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE  = 0x0005;
    83 
    84     /**
    85      * The sharpening as set in the camera.
    86      *
    87      * Values observed
    88      * - AUTO
    89      * - NORMAL (D70)
    90      * - NONE (D1X)
    91      */
    92     public static final int TAG_NIKON_TYPE2_CAMERA_SHARPENING = 0x0006;
    93 
    94     /**
    95      * The auto-focus type used by the camera.
    96      *
    97      * Values observed
    98      * - AF-S
    99      * - AF-C
    100      * - MANUAL
    101      */
    102     public static final int TAG_NIKON_TYPE2_AF_TYPE = 0x0007;
    103 
    104     /**
    105      * Values observed
    106      * - NORMAL
    107      * - RED-EYE
    108      *
    109      * Note: when TAG_NIKON_TYPE2_AUTO_FLASH_MODE is blank, Nikon Browser displays "Flash Sync Mode: Not Attached"
    110      */
    111     public static final int TAG_NIKON_TYPE2_FLASH_SYNC_MODE = 0x0008;
    112 
    113     /**
    114      * Values observed
    115      * - Built-in,TTL
    116      * - Optional,TTL (with speedlight SB800, flash sync mode as NORMAL.  NikonBrowser reports Auto Flash Comp: 0 EV -- which tag is that?) (D70)
    117      * - NEW_TTL (Nikon Browser interprets as "D-TTL")
    118      * - (blank -- accompanied FlashSyncMode of NORMAL) (D70)
    119      */
    120     public static final int TAG_NIKON_TYPE2_AUTO_FLASH_MODE = 0x0009;
    121 
    122     /**
    123      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    124      */
    125     public static final int TAG_NIKON_TYPE2_UNKNOWN_34 = 0x000A;
    126 
    127     /**
    128      * Values observed
    129      * - 0
    130      */
    131     public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE = 0x000B;
    132 
    133     /**
    134      * The first two numbers are coefficients to multiply red and blue channels according to white balance as set in the
    135      * camera. The meaning of the third and the fourth numbers is unknown.
    136      *
    137      * Values observed
    138      * - 2.25882352 1.76078431 0.0 0.0
    139      * - 10242/1 34305/1 0/1 0/1
    140      * - 234765625/100000000 1140625/1000000 1/1 1/1
    141      */
    142     public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF = 0x000C;
    143 
    144     /**
    145      * Values observed
    146      * - 0,1,6,0 (hex)
    147      */
    148     public static final int TAG_NIKON_TYPE2_UNKNOWN_1 = 0x000D;
    149 
    150     /**
    151      * Values observed
    152      * - 0,1,c,0 (hex)
    153      */
    154     public static final int TAG_NIKON_TYPE2_UNKNOWN_2 = 0x000E;
    155 
    156     /**
    157      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    158      */
    159     public static final int TAG_NIKON_TYPE2_ISO_SELECTION = 0x000F;
    160 
    161     /**
    162      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    163      */
    164     public static final int TAG_NIKON_TYPE2_DATA_DUMP = 0x0010;
    165 
    166     /**
    167      * Values observed
    168      * - 914
    169      * - 1379 (D70)
    170      * - 2781 (D1X)
    171      * - 6942 (D100)
    172      */
    173     public static final int TAG_NIKON_TYPE2_UNKNOWN_3 = 0x0011;
    174 
    175     /**
    176      * Values observed
    177      * - (no value -- blank)
    178      */
    179     public static final int TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION = 0x0012;
    180 
    181     /**
    182      * Values observed
    183      * - 0 250
    184      * - 0 400
    185      */
    186     public static final int TAG_NIKON_TYPE2_ISO_2 = 0x0013;
    187 
    188     /**
    189      * Values observed
    190      * - 0 0 49163 53255
    191      * - 0 0 3008 2000 (the image dimensions were 3008x2000) (D70)
    192      */
    193     public static final int TAG_NIKON_TYPE2_UNKNOWN_21 = 0x0016;
    194 
    195     /**
    196      * Values observed
    197      * - (blank)
    198      */
    199     public static final int TAG_NIKON_TYPE2_UNKNOWN_22 = 0x0017;
    200 
    201     /**
    202      * Values observed
    203      * - (blank)
    204      */
    205     public static final int TAG_NIKON_TYPE2_UNKNOWN_23 = 0x0018;
    206 
    207     /**
    208      * Values observed
    209      * - 0
    210      */
    211     public static final int TAG_NIKON_TYPE2_UNKNOWN_24 = 0x0019;
    212 
    213     /**
    214      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    215      */
    216     public static final int TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT = 0x0080;
    217 
    218     /**
    219      * The tone compensation as set in the camera.
    220      *
    221      * Values observed
    222      * - AUTO
    223      * - NORMAL (D1X, D100)
    224      */
    225     public static final int TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION = 0x0081;
    226 
    227     /**
    228      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    229      */
    230     public static final int TAG_NIKON_TYPE2_ADAPTER = 0x0082;
    231 
    232     /**
    233      * Values observed
    234      * - 6
    235      * - 6 (D70)
    236      * - 2 (D1X)
    237      */
    238     public static final int TAG_NIKON_TYPE2_UNKNOWN_4 = 0x0083;
    239 
    240     /**
    241      * A pair of focal/max-fstop values that describe the lens used.
    242      *
    243      * Values observed
    244      * - 180.0,180.0,2.8,2.8 (D100)
    245      * - 240/10 850/10 35/10 45/10
    246      * - 18-70mm f/3.5-4.5 (D70)
    247      * - 17-35mm f/2.8-2.8 (D1X)
    248      * - 70-200mm f/2.8-2.8 (D70)
    249      *
    250      * Nikon Browser identifies the lens as "18-70mm F/3.5-4.5 G" which
    251      * is identical to metadata extractor, except for the "G".  This must
    252      * be coming from another tag...
    253      */
    254     public static final int TAG_NIKON_TYPE2_LENS = 0x0084;
    255 
    256     /**
    257      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    258      */
    259     public static final int TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE = 0x0085;
    260 
    261     /**
    262      * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
    263      */
    264     public static final int TAG_NIKON_TYPE2_DIGITAL_ZOOM = 0x0086;
    265 
    266     /**
    267      * Values observed
    268      * - 0
    269      * - 9
    270      * - 3 (D1X)
    271      */
    272     public static final int TAG_NIKON_TYPE2_UNKNOWN_5 = 0x0087;
    273 
    274     /**
    275      * Values observed
    276      * -
    277      */
    278     public static final int TAG_NIKON_TYPE2_AF_FOCUS_POSITION = 0x0088;
    279 
    280     /**
    281      * Values observed
    282      * - 0
    283      * - 1
    284      */
    285     public static final int TAG_NIKON_TYPE2_UNKNOWN_7 = 0x0089;
    286 
    287     /**
    288      * Values observed
    289      * - 0
    290      * - 0
    291      */
    292     public static final int TAG_NIKON_TYPE2_UNKNOWN_20 = 0x008A;
    293 
    294     /**
    295      * Values observed
    296      * - 48,1,c,0 (hex) (D100)
    297      * - @ <hex>
    298      */
    299     public static final int TAG_NIKON_TYPE2_UNKNOWN_8 = 0x008B;
    300 
    301     /**
    302      * Unknown.  Fabrizio believes this may be a lookup table for the user-defined curve.
    303      *
    304      * Values observed
    305      * - (blank) (D1X)
    306      */
    307     public static final int TAG_NIKON_TYPE2_UNKNOWN_9 = 0x008C;
    308 
    309     /**
    310      * The color space as set in the camera.
    311      *
    312      * Values observed
    313      * - MODE1
    314      * - Mode I (sRGB) (D70)
    315      * - MODE2 (D1X, D100)
    316      */
    317     public static final int TAG_NIKON_TYPE2_CAMERA_COLOR_MODE = 0x008D;
    318 
    319     /**
    320      * Values observed
    321      * - NATURAL
    322      * - SPEEDLIGHT (D70, D1X)
    323      */
    324     public static final int TAG_NIKON_TYPE2_LIGHT_SOURCE = 0x0090;
    325 
    326     /**
    327      * Values observed
    328      * - 0100 <hex>
    329      * - 0103 (D70)
    330      * - 0100 (D1X)
    331      */
    332     public static final int TAG_NIKON_TYPE2_UNKNOWN_11 = 0x0091;
    333 
    334     /**
    335      * The hue adjustment as set in the camera.
    336      *
    337      * Values observed
    338      * - 0
    339      */
    340     public static final int TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT = 0x0092;
    341 
    342     /**
    343      * Values observed
    344      * - OFF
    345      */
    346     public static final int TAG_NIKON_TYPE2_NOISE_REDUCTION = 0x0095;
    347 
    348     /**
    349      * Values observed
    350      * - 0100 <hex>
    351      * - 0103 <hex>
    352      */
    353     public static final int TAG_NIKON_TYPE2_UNKNOWN_12 = 0x0097;
    354 
    355     /**
    356      * Values observed
    357      * - 0100 <hex>
    358      * - 0101 <hex>
    359      * - 0100 <hex> (D1X)
    360      * - 30,31,30,30,0,0,b,48,7c,7c,24,24,5,15,24,0,0,0,0,0 (hex) (D100)
    361      */
    362     public static final int TAG_NIKON_TYPE2_UNKNOWN_13 = 0x0098;
    363 
    364     /**
    365      * Values observed
    366      * - 2014 662 (D1X)
    367      * - 1517,1012 (D100)
    368      */
    369     public static final int TAG_NIKON_TYPE2_UNKNOWN_14 = 0x0099;
    370 
    371     /**
    372      * Values observed
    373      * - 78/10 78/10
    374      * - 78/10 78/10 (D70)
    375      * - 59/10 59/5 (D1X)
    376      * - 7.8,7.8 (D100)
    377      */
    378     public static final int TAG_NIKON_TYPE2_UNKNOWN_15 = 0x009A;
    379 
    380     /**
    381      * Values observed
    382      * - NO= 00002539
    383      */
    384     public static final int TAG_NIKON_TYPE2_UNKNOWN_25 = 0x00A0;
    385 
    386     /**
    387      * Values observed
    388      * - 1564851
    389      */
    390     public static final int TAG_NIKON_TYPE2_UNKNOWN_26 = 0x00A2;
    391 
    392     /**
    393      * Values observed
    394      * - 0
    395      */
    396     public static final int TAG_NIKON_TYPE2_UNKNOWN_27 = 0x00A3;
    397 
    398     /**
    399      * This appears to be a sequence number to indentify the exposure.  This value seems to increment
    400      * for consecutive exposures (observed on D70).
    401      *
    402      * Values observed
    403      * - 5062
    404      */
    405     public static final int TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER = 0x00A7;
    406 
    407     /**
    408      * Values observed
    409      * - 0100 (D70)
    410      */
    411     public static final int TAG_NIKON_TYPE2_UNKNOWN_32 = 0x00A8;
    412 
    413     /**
    414      * Values observed
    415      * - NORMAL (D70)
    416      */
    417     public static final int TAG_NIKON_TYPE2_UNKNOWN_33 = 0x00A9;
    418 
    419     /**
    420      * Nikon Browser suggests this value represents Saturation...
    421      * Values observed
    422      * - NORMAL (D70)
    423      */
    424     public static final int TAG_NIKON_TYPE2_UNKNOWN_29 = 0x00AA;
    425 
    426     /**
    427      * Values observed
    428      * - AUTO (D70)
    429      * - (blank) (D70)
    430      */
    431     public static final int TAG_NIKON_TYPE2_UNKNOWN_30 = 0x00AB;
    432 
    433     /**
    434      * Data about changes set by Nikon Capture Editor.
    435      *
    436      * Values observed
    437      */
    438     public static final int TAG_NIKON_TYPE2_CAPTURE_EDITOR_DATA = 0x0E01;
    439 
    440     /**
    441      * Values observed
    442      * - 1473
    443      * - 7036 (D100)
    444      */
    445     public static final int TAG_NIKON_TYPE2_UNKNOWN_16 = 0x0E10;
    446 
    447     protected static final HashMap _tagNameMap = new HashMap();
    448 
    449     static
    450     {
    451         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_FIRMWARE_VERSION), "Firmware Version");
    452         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_1), "ISO");
    453         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT), "Quality & File Format");
    454         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE), "White Balance");
    455         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_SHARPENING), "Sharpening");
    456         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AF_TYPE), "AF Type");
    457         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE), "White Balance Fine");
    458         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF), "White Balance RB Coefficients");
    459         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_2), "ISO");
    460         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_SELECTION), "ISO Selection");
    461         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_DATA_DUMP), "Data Dump");
    462         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT), "Image Adjustment");
    463         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION), "Tone Compensation");
    464         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ADAPTER), "Adapter");
    465         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_LENS), "Lens");
    466         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE), "Manual Focus Distance");
    467         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_DIGITAL_ZOOM), "Digital Zoom");
    468         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_COLOR_MODE), "Colour Mode");
    469         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT), "Camera Hue Adjustment");
    470         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_NOISE_REDUCTION), "Noise Reduction");
    471         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAPTURE_EDITOR_DATA), "Capture Editor Data");
    472 
    473         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_1), "Unknown 01");
    474         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_2), "Unknown 02");
    475         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_3), "Unknown 03");
    476         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_4), "Unknown 04");
    477         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_5), "Unknown 05");
    478         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AF_FOCUS_POSITION), "AF Focus Position");
    479         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_7), "Unknown 07");
    480         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_8), "Unknown 08");
    481         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_9), "Unknown 09");
    482         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_LIGHT_SOURCE), "Light source");
    483         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_11), "Unknown 11");
    484         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_12), "Unknown 12");
    485         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_13), "Unknown 13");
    486         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_14), "Unknown 14");
    487         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_15), "Unknown 15");
    488         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_16), "Unknown 16");
    489         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_FLASH_SYNC_MODE), "Flash Sync Mode");
    490         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AUTO_FLASH_MODE), "Auto Flash Mode");
    491         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION), "Auto Flash Compensation");
    492         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER), "Exposure Sequence Number");
    493         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_COLOR_MODE), "Color Mode");
    494 
    495         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_20), "Unknown 20");
    496         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_21), "Unknown 21");
    497         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_22), "Unknown 22");
    498         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_23), "Unknown 23");
    499         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_24), "Unknown 24");
    500         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_25), "Unknown 25");
    501         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_26), "Unknown 26");
    502         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_27), "Unknown 27");
    503         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_29), "Unknown 29");
    504         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_30), "Unknown 30");
    505         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_32), "Unknown 32");
    506         _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_33), "Unknown 33");
    507     }
    508 
    509     public NikonType2MakernoteDirectory()
    510     {
    511         this.setDescriptor(new NikonType2MakernoteDescriptor(this));
    512     }
    513 
    514     public Rational getAutoFlashCompensation() throws MetadataException
    515     {
    516         if (!containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION))
    517             return null;
    518 
    519         byte[] bytes = getByteArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION);
    520         return CalculateFlashCompensationFromBytes(bytes);
    521     }
    522 
    523     public static Rational CalculateFlashCompensationFromBytes(byte[] bytes)
    524     {
    525         if (bytes.length==3)
    526         {
    527             byte denominator = bytes[2];
    528             int numerator = (int)bytes[0] * bytes[1];
    529             return new Rational(numerator, denominator);
    530         }
    531         return null;
    532     }
    533 
    534     public String getName()
    535     {
    536         return "Nikon Makernote";
    537     }
    538 
    539     protected HashMap getTagNameMap()
    540     {
    541         return _tagNameMap;
    542     }
    543 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.metadata.Directory;
     25
     26import java.util.HashMap;
     27
     28/**
     29 * Describes tags specific to Nikon (type 2) cameras.  Type-2 applies to the E990 and D-series cameras such as the E990, D1,
     30 * D70 and D100.
     31 * <p/>
     32 * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data.
     33 * http://www.timelesswanderings.net/equipment/D100/NEF.html
     34 * <p/>
     35 * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute.  Similarly
     36 * image hiding uses the 'hidden' attribute (observed on the D70).  Consequently, these values are not available here.
     37 * <p/>
     38 * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field.
     39 * New tags have subsequently been added since Fabrizio's observations.
     40 * <p/>
     41 * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD.  In
     42 * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also
     43 * provide this prefix to aid in software decoding).
     44 *
     45 * @author Drew Noakes http://drewnoakes.com
     46 */
     47public class NikonType2MakernoteDirectory extends Directory
     48{
     49    /**
     50     * Values observed
     51     * - 0200 (D70)
     52     * - 0200 (D1X)
     53     */
     54    public static final int TAG_NIKON_TYPE2_FIRMWARE_VERSION = 0x0001;
     55
     56    /**
     57     * Values observed
     58     * - 0 250
     59     * - 0 400
     60     */
     61    public static final int TAG_NIKON_TYPE2_ISO_1 = 0x0002;
     62
     63    /**
     64     * The camera's color mode, as an uppercase string.  Examples include:
     65     * <ul>
     66     * <li><code>B & W</code></li>
     67     * <li><code>COLOR</code></li>
     68     * <li><code>COOL</code></li>
     69     * <li><code>SEPIA</code></li>
     70     * <li><code>VIVID</code></li>
     71     * </ul>
     72     */
     73    public static final int TAG_NIKON_TYPE2_COLOR_MODE = 0x0003;
     74
     75    /**
     76     * The camera's quality setting, as an uppercase string.  Examples include:
     77     * <ul>
     78     * <li><code>BASIC</code></li>
     79     * <li><code>FINE</code></li>
     80     * <li><code>NORMAL</code></li>
     81     * <li><code>RAW</code></li>
     82     * <li><code>RAW2.7M</code></li>
     83     * </ul>
     84     */
     85    public static final int TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT = 0x0004;
     86
     87    /**
     88     * The camera's white balance setting, as an uppercase string.  Examples include:
     89     *
     90     * <ul>
     91     * <li><code>AUTO</code></li>
     92     * <li><code>CLOUDY</code></li>
     93     * <li><code>FLASH</code></li>
     94     * <li><code>FLUORESCENT</code></li>
     95     * <li><code>INCANDESCENT</code></li>
     96     * <li><code>PRESET</code></li>
     97     * <li><code>PRESET0</code></li>
     98     * <li><code>PRESET1</code></li>
     99     * <li><code>PRESET3</code></li>
     100     * <li><code>SUNNY</code></li>
     101     * <li><code>WHITE PRESET</code></li>
     102     * <li><code>4350K</code></li>
     103     * <li><code>5000K</code></li>
     104     * <li><code>DAY WHITE FL</code></li>
     105     * <li><code>SHADE</code></li>
     106     * </ul>
     107     */
     108    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE  = 0x0005;
     109
     110    /**
     111     * The camera's sharpening setting, as an uppercase string.  Examples include:
     112     *
     113     * <ul>
     114     * <li><code>AUTO</code></li>
     115     * <li><code>HIGH</code></li>
     116     * <li><code>LOW</code></li>
     117     * <li><code>NONE</code></li>
     118     * <li><code>NORMAL</code></li>
     119     * <li><code>MED.H</code></li>
     120     * <li><code>MED.L</code></li>
     121     * </ul>
     122     */
     123    public static final int TAG_NIKON_TYPE2_CAMERA_SHARPENING = 0x0006;
     124
     125    /**
     126     * The camera's auto-focus mode, as an uppercase string.  Examples include:
     127     *
     128     * <ul>
     129     * <li><code>AF-C</code></li>
     130     * <li><code>AF-S</code></li>
     131     * <li><code>MANUAL</code></li>
     132     * <li><code>AF-A</code></li>
     133     * </ul>
     134     */
     135    public static final int TAG_NIKON_TYPE2_AF_TYPE = 0x0007;
     136
     137    /**
     138     * The camera's flash setting, as an uppercase string.  Examples include:
     139     *
     140     * <ul>
     141     * <li><code></code></li>
     142     * <li><code>NORMAL</code></li>
     143     * <li><code>RED-EYE</code></li>
     144     * <li><code>SLOW</code></li>
     145     * <li><code>NEW_TTL</code></li>
     146     * <li><code>REAR</code></li>
     147     * <li><code>REAR SLOW</code></li>
     148     * </ul>
     149     * Note: when TAG_NIKON_TYPE2_AUTO_FLASH_MODE is blank (whitespace), Nikon Browser displays "Flash Sync Mode: Not Attached"
     150     */
     151    public static final int TAG_NIKON_TYPE2_FLASH_SYNC_MODE = 0x0008;
     152
     153    /**
     154     * The type of flash used in the photograph, as a string.  Examples include:
     155     *
     156     * <ul>
     157     * <li><code></code></li>
     158     * <li><code>Built-in,TTL</code></li>
     159     * <li><code>NEW_TTL</code> Nikon Browser interprets as "D-TTL"</li>
     160     * <li><code>Built-in,M</code></li>
     161     * <li><code>Optional,TTL</code> with speedlight SB800, flash sync mode as "NORMAL"</li>
     162     * </ul>
     163     */
     164    public static final int TAG_NIKON_TYPE2_AUTO_FLASH_MODE = 0x0009;
     165
     166    /**
     167     * An unknown tag, as a rational.  Several values given here:
     168     * http://gvsoft.homedns.org/exif/makernote-nikon-type2.html#0x000b
     169     */
     170    public static final int TAG_NIKON_TYPE2_UNKNOWN_34 = 0x000A;
     171
     172    /**
     173     * The camera's white balance bias setting, as an uint16 array having either one or two elements.
     174     *
     175     * <ul>
     176     * <li><code>0</code></li>
     177     * <li><code>1</code></li>
     178     * <li><code>-3</code></li>
     179     * <li><code>-2</code></li>
     180     * <li><code>-1</code></li>
     181     * <li><code>0,0</code></li>
     182     * <li><code>1,0</code></li>
     183     * <li><code>5,-5</code></li>
     184     * </ul>
     185     */
     186    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE = 0x000B;
     187
     188    /**
     189     * The first two numbers are coefficients to multiply red and blue channels according to white balance as set in the
     190     * camera. The meaning of the third and the fourth numbers is unknown.
     191     *
     192     * Values observed
     193     * - 2.25882352 1.76078431 0.0 0.0
     194     * - 10242/1 34305/1 0/1 0/1
     195     * - 234765625/100000000 1140625/1000000 1/1 1/1
     196     */
     197    public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF = 0x000C;
     198
     199    /**
     200     * The camera's program shift setting, as an array of four integers.
     201     * The value, in EV, is calculated as <code>a*b/c</code>.
     202     *
     203     * <ul>
     204     * <li><code>0,1,3,0</code> = 0 EV</li>
     205     * <li><code>1,1,3,0</code> = 0.33 EV</li>
     206     * <li><code>-3,1,3,0</code> = -1 EV</li>
     207     * <li><code>1,1,2,0</code> = 0.5 EV</li>
     208     * <li><code>2,1,6,0</code> = 0.33 EV</li>
     209     * </ul>
     210     */
     211    public static final int TAG_NIKON_TYPE2_PROGRAM_SHIFT = 0x000D;
     212
     213    /**
     214     * The exposure difference, as an array of four integers.
     215     * The value, in EV, is calculated as <code>a*b/c</code>.
     216     *
     217     * <ul>
     218     * <li><code>-105,1,12,0</code> = -8.75 EV</li>
     219     * <li><code>-72,1,12,0</code> = -6.00 EV</li>
     220     * <li><code>-11,1,12,0</code> = -0.92 EV</li>
     221     * </ul>
     222     */
     223    public static final int TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE = 0x000E;
     224
     225    /**
     226     * The camera's ISO mode, as an uppercase string.
     227     *
     228     * <ul>
     229     * <li><code>AUTO</code></code></li>
     230     * <li><code>MANUAL</code></li>
     231     * </ul>
     232     */
     233    public static final int TAG_NIKON_TYPE2_ISO_MODE = 0x000F;
     234
     235    /**
     236     * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     237     */
     238    public static final int TAG_NIKON_TYPE2_DATA_DUMP = 0x0010;
     239
     240    /**
     241     * Preview to another IFD (?)
     242     * <p/>
     243     * Details here: http://gvsoft.homedns.org/exif/makernote-nikon-2-tag0x0011.html
     244     * // TODO if this is another IFD, decode it
     245     */
     246    public static final int TAG_NIKON_TYPE2_PREVIEW_IFD = 0x0011;
     247
     248    /**
     249     * The flash compensation, as an array of four integers.
     250     * The value, in EV, is calculated as <code>a*b/c</code>.
     251     *
     252     * <ul>
     253     * <li><code>-18,1,6,0</code> = -3 EV</li>
     254     * <li><code>4,1,6,0</code> = 0.67 EV</li>
     255     * <li><code>6,1,6,0</code> = 1 EV</li>
     256     * </ul>
     257     */
     258    public static final int TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION = 0x0012;
     259
     260    /**
     261     * The requested ISO value, as an array of two integers.
     262     *
     263     * <ul>
     264     * <li><code>0,0</code></li>
     265     * <li><code>0,125</code></li>
     266     * <li><code>1,2500</code></li>
     267     * </ul>
     268     */
     269    public static final int TAG_NIKON_TYPE2_ISO_REQUESTED = 0x0013;
     270
     271    /**
     272     * Defines the photo corner coordinates, in 8 bytes.  Treated as four 16-bit integers, they
     273     * decode as: top-left (x,y); bot-right (x,y)
     274     * - 0 0 49163 53255
     275     * - 0 0 3008 2000 (the image dimensions were 3008x2000) (D70)
     276     * <ul>
     277     * <li><code>0,0,4288,2848</code> The max resolution of the D300 camera</li>
     278     * <li><code>0,0,3008,2000</code> The max resolution of the D70 camera</li>
     279     * <li><code>0,0,4256,2832</code> The max resolution of the D3 camera</li>
     280     * </ul>
     281     */
     282    public static final int TAG_NIKON_TYPE2_IMAGE_BOUNDARY = 0x0016;
     283
     284    /**
     285     * The flash exposure compensation, as an array of four integers.
     286     * The value, in EV, is calculated as <code>a*b/c</code>.
     287     *
     288     * <ul>
     289     * <li><code>0,0,0,0</code> = 0 EV</li>
     290     * <li><code>0,1,6,0</code> = 0 EV</li>
     291     * <li><code>4,1,6,0</code> = 0.67 EV</li>
     292     * </ul>
     293     */
     294    public static final int TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION = 0x0017;
     295
     296    /**
     297     * The flash bracket compensation, as an array of four integers.
     298     * The value, in EV, is calculated as <code>a*b/c</code>.
     299     *
     300     * <ul>
     301     * <li><code>0,0,0,0</code> = 0 EV</li>
     302     * <li><code>0,1,6,0</code> = 0 EV</li>
     303     * <li><code>4,1,6,0</code> = 0.67 EV</li>
     304     * </ul>
     305     */
     306    public static final int TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION = 0x0018;
     307
     308    /**
     309     * The AE bracket compensation, as a rational number.
     310     *
     311     * <ul>
     312     * <li><code>0/0</code></li>
     313     * <li><code>0/1</code></li>
     314     * <li><code>0/6</code></li>
     315     * <li><code>4/6</code></li>
     316     * <li><code>6/6</code></li>
     317     * </ul>
     318     */
     319    public static final int TAG_NIKON_TYPE2_AE_BRACKET_COMPENSATION = 0x0019;
     320
     321    /**
     322     * Flash mode, as a string.
     323     *
     324     * <ul>
     325     * <li><code></code></li>
     326     * <li><code>Red Eye Reduction</code></li>
     327     * <li><code>D-Lighting</code></li>
     328     * <li><code>Distortion control</code></li>
     329     * </ul>
     330     */
     331    public static final int TAG_NIKON_TYPE2_FLASH_MODE = 0x001a;
     332
     333    public static final int TAG_NIKON_TYPE2_CROP_HIGH_SPEED = 0x001b;
     334    public static final int TAG_NIKON_TYPE2_EXPOSURE_TUNING = 0x001c;
     335
     336    /**
     337     * The camera's serial number, as a string.
     338     * Note that D200 is always blank, and D50 is always <code>"D50"</code>.
     339     */
     340    public static final int TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER = 0x001d;
     341
     342    /**
     343     * The camera's color space setting.
     344     *
     345     * <ul>
     346     * <li><code>1</code> sRGB</li>
     347     * <li><code>2</code> Adobe RGB</li>
     348     * </ul>
     349     */
     350    public static final int TAG_NIKON_TYPE2_COLOR_SPACE = 0x001e;
     351    public static final int TAG_NIKON_TYPE2_VR_INFO = 0x001f;
     352    public static final int TAG_NIKON_TYPE2_IMAGE_AUTHENTICATION = 0x0020;
     353    public static final int TAG_NIKON_TYPE2_UNKNOWN_35 = 0x0021;
     354
     355    /**
     356     * The active D-Lighting setting.
     357     *
     358     * <ul>
     359     * <li><code>0</code> Off</li>
     360     * <li><code>1</code> Low</li>
     361     * <li><code>3</code> Normal</li>
     362     * <li><code>5</code> High</li>
     363     * <li><code>7</code> Extra High</li>
     364     * <li><code>65535</code> Auto</li>
     365     * </ul>
     366     */
     367    public static final int TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING = 0x0022;
     368    public static final int TAG_NIKON_TYPE2_PICTURE_CONTROL = 0x0023;
     369    public static final int TAG_NIKON_TYPE2_WORLD_TIME = 0x0024;
     370    public static final int TAG_NIKON_TYPE2_ISO_INFO = 0x0025;
     371    public static final int TAG_NIKON_TYPE2_UNKNOWN_36 = 0x0026;
     372    public static final int TAG_NIKON_TYPE2_UNKNOWN_37 = 0x0027;
     373    public static final int TAG_NIKON_TYPE2_UNKNOWN_38 = 0x0028;
     374    public static final int TAG_NIKON_TYPE2_UNKNOWN_39 = 0x0029;
     375
     376    /**
     377     * The camera's vignette control setting.
     378     *
     379     * <ul>
     380     * <li><code>0</code> Off</li>
     381     * <li><code>1</code> Low</li>
     382     * <li><code>3</code> Normal</li>
     383     * <li><code>5</code> High</li>
     384     * </ul>
     385     */
     386    public static final int TAG_NIKON_TYPE2_VIGNETTE_CONTROL = 0x002a;
     387    public static final int TAG_NIKON_TYPE2_UNKNOWN_40 = 0x002b;
     388    public static final int TAG_NIKON_TYPE2_UNKNOWN_41 = 0x002c;
     389    public static final int TAG_NIKON_TYPE2_UNKNOWN_42 = 0x002d;
     390    public static final int TAG_NIKON_TYPE2_UNKNOWN_43 = 0x002e;
     391    public static final int TAG_NIKON_TYPE2_UNKNOWN_44 = 0x002f;
     392    public static final int TAG_NIKON_TYPE2_UNKNOWN_45 = 0x0030;
     393    public static final int TAG_NIKON_TYPE2_UNKNOWN_46 = 0x0031;
     394
     395    /**
     396     * The camera's image adjustment setting, as a string.
     397     *
     398     * <ul>
     399     * <li><code>AUTO</code></li>
     400     * <li><code>CONTRAST(+)</code></li>
     401     * <li><code>CONTRAST(-)</code></li>
     402     * <li><code>NORMAL</code></li>
     403     * <li><code>B & W</code></li>
     404     * <li><code>BRIGHTNESS(+)</code></li>
     405     * <li><code>BRIGHTNESS(-)</code></li>
     406     * <li><code>SEPIA</code></li>
     407     * </ul>
     408     */
     409    public static final int TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT = 0x0080;
     410
     411    /**
     412     * The camera's tone compensation setting, as a string.
     413     *
     414     * <ul>
     415     * <li><code>NORMAL</code></li>
     416     * <li><code>LOW</code></li>
     417     * <li><code>MED.L</code></li>
     418     * <li><code>MED.H</code></li>
     419     * <li><code>HIGH</code></li>
     420     * <li><code>AUTO</code></li>
     421     * </ul>
     422     */
     423    public static final int TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION = 0x0081;
     424
     425    /**
     426     * A description of any auxiliary lens, as a string.
     427     *
     428     * <ul>
     429     * <li><code>OFF</code></li>
     430     * <li><code>FISHEYE 1</code></li>
     431     * <li><code>FISHEYE 2</code></li>
     432     * <li><code>TELEPHOTO 2</code></li>
     433     * <li><code>WIDE ADAPTER</code></li>
     434     * </ul>
     435     */
     436    public static final int TAG_NIKON_TYPE2_ADAPTER = 0x0082;
     437
     438    /**
     439     * The type of lens used, as a byte.
     440     *
     441     * <ul>
     442     * <li><code>0x00</code> AF</li>
     443     * <li><code>0x01</code> MF</li>
     444     * <li><code>0x02</code> D</li>
     445     * <li><code>0x06</code> G, D</li>
     446     * <li><code>0x08</code> VR</li>
     447     * <li><code>0x0a</code> VR, D</li>
     448     * <li><code>0x0e</code> VR, G, D</li>
     449     * </ul>
     450     */
     451    public static final int TAG_NIKON_TYPE2_LENS_TYPE = 0x0083;
     452
     453    /**
     454     * A pair of focal/max-fstop values that describe the lens used.
     455     *
     456     * Values observed
     457     * - 180.0,180.0,2.8,2.8 (D100)
     458     * - 240/10 850/10 35/10 45/10
     459     * - 18-70mm f/3.5-4.5 (D70)
     460     * - 17-35mm f/2.8-2.8 (D1X)
     461     * - 70-200mm f/2.8-2.8 (D70)
     462     *
     463     * Nikon Browser identifies the lens as "18-70mm F/3.5-4.5 G" which
     464     * is identical to metadata extractor, except for the "G".  This must
     465     * be coming from another tag...
     466     */
     467    public static final int TAG_NIKON_TYPE2_LENS = 0x0084;
     468
     469    /**
     470     * Added during merge of Type2 & Type3.  May apply to earlier models, such as E990 and D1.
     471     */
     472    public static final int TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE = 0x0085;
     473
     474    /**
     475     * The amount of digital zoom used.
     476     */
     477    public static final int TAG_NIKON_TYPE2_DIGITAL_ZOOM = 0x0086;
     478
     479    /**
     480     * Whether the flash was used in this image.
     481     *
     482     * <ul>
     483     * <li><code>0</code> Flash Not Used</li>
     484     * <li><code>1</code> Manual Flash</li>
     485     * <li><code>3</code> Flash Not Ready</li>
     486     * <li><code>7</code> External Flash</li>
     487     * <li><code>8</code> Fired, Commander Mode</li>
     488     * <li><code>9</code> Fired, TTL Mode</li>
     489     * </ul>
     490     */
     491    public static final int TAG_NIKON_TYPE2_FLASH_USED = 0x0087;
     492
     493    /**
     494     * The position of the autofocus target.
     495     */
     496    public static final int TAG_NIKON_TYPE2_AF_FOCUS_POSITION = 0x0088;
     497
     498    /**
     499     * The camera's shooting mode.
     500     * <p/>
     501     * A bit-array with:
     502     * <ul>
     503     * <li><code>0</code> Single Frame</li>
     504     * <li><code>1</code> Continuous</li>
     505     * <li><code>2</code> Delay</li>
     506     * <li><code>8</code> PC Control</li>
     507     * <li><code>16</code> Exposure Bracketing</li>
     508     * <li><code>32</code> Auto ISO</li>
     509     * <li><code>64</code> White-Balance Bracketing</li>
     510     * <li><code>128</code> IR Control</li>
     511     * </ul>
     512     */
     513    public static final int TAG_NIKON_TYPE2_SHOOTING_MODE = 0x0089;
     514
     515    public static final int TAG_NIKON_TYPE2_UNKNOWN_20 = 0x008A;
     516
     517    /**
     518     * Lens stops, as an array of four integers.
     519     * The value, in EV, is calculated as <code>a*b/c</code>.
     520     *
     521     * <ul>
     522     * <li><code>64,1,12,0</code> = 5.33 EV</li>
     523     * <li><code>72,1,12,0</code> = 6 EV</li>
     524     * </ul>
     525     */
     526    public static final int TAG_NIKON_TYPE2_LENS_STOPS = 0x008B;
     527
     528    public static final int TAG_NIKON_TYPE2_CONTRAST_CURVE = 0x008C;
     529
     530    /**
     531     * The color space as set in the camera, as a string.
     532     *
     533     * <ul>
     534     * <li><code>MODE1</code> = Mode 1 (sRGB)</li>
     535     * <li><code>MODE1a</code> = Mode 1 (sRGB)</li>
     536     * <li><code>MODE2</code> = Mode 2 (Adobe RGB)</li>
     537     * <li><code>MODE3</code> = Mode 2 (sRGB): Higher Saturation</li>
     538     * <li><code>MODE3a</code> = Mode 2 (sRGB): Higher Saturation</li>
     539     * <li><code>B & W</code> = B & W</li>
     540     * </ul>
     541     */
     542    public static final int TAG_NIKON_TYPE2_CAMERA_COLOR_MODE = 0x008D;
     543    public static final int TAG_NIKON_TYPE2_UNKNOWN_47 = 0x008E;
     544
     545    /**
     546     * The camera's scene mode, as a string.  Examples include:
     547     * <ul>
     548     * <li><code>BEACH/SNOW</code></li>
     549     * <li><code>CLOSE UP</code></li>
     550     * <li><code>NIGHT PORTRAIT</code></li>
     551     * <li><code>PORTRAIT</code></li>
     552     * <li><code>ANTI-SHAKE</code></li>
     553     * <li><code>BACK LIGHT</code></li>
     554     * <li><code>BEST FACE</code></li>
     555     * <li><code>BEST</code></li>
     556     * <li><code>COPY</code></li>
     557     * <li><code>DAWN/DUSK</code></li>
     558     * <li><code>FACE-PRIORITY</code></li>
     559     * <li><code>FIREWORKS</code></li>
     560     * <li><code>FOOD</code></li>
     561     * <li><code>HIGH SENS.</code></li>
     562     * <li><code>LAND SCAPE</code></li>
     563     * <li><code>MUSEUM</code></li>
     564     * <li><code>PANORAMA ASSIST</code></li>
     565     * <li><code>PARTY/INDOOR</code></li>
     566     * <li><code>SCENE AUTO</code></li>
     567     * <li><code>SMILE</code></li>
     568     * <li><code>SPORT</code></li>
     569     * <li><code>SPORT CONT.</code></li>
     570     * <li><code>SUNSET</code></li>
     571     * </ul>
     572     */
     573    public static final int TAG_NIKON_TYPE2_SCENE_MODE = 0x008F;
     574
     575    /**
     576     * The lighting type, as a string.  Examples include:
     577     * <ul>
     578     * <li><code></code></li>
     579     * <li><code>NATURAL</code></li>
     580     * <li><code>SPEEDLIGHT</code></li>
     581     * <li><code>COLORED</code></li>
     582     * <li><code>MIXED</code></li>
     583     * <li><code>NORMAL</code></li>
     584     * </ul>
     585     */
     586    public static final int TAG_NIKON_TYPE2_LIGHT_SOURCE = 0x0090;
     587
     588    /**
     589     * Advertised as ASCII, but actually isn't.  A variable number of bytes (eg. 18 to 533).  Actual number of bytes
     590     * appears fixed for a given camera model.
     591     */
     592    public static final int TAG_NIKON_TYPE2_SHOT_INFO = 0x0091;
     593
     594    /**
     595     * The hue adjustment as set in the camera.  Values observed are either 0 or 3.
     596     */
     597    public static final int TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT = 0x0092;
     598    /**
     599     * The NEF (RAW) compression.  Examples include:
     600     * <ul>
     601     * <li><code>1</code> Lossy (Type 1)</li>
     602     * <li><code>2</code> Uncompressed</li>
     603     * <li><code>3</code> Lossless</li>
     604     * <li><code>4</code> Lossy (Type 2)</li>
     605     * </ul>
     606     */
     607    public static final int TAG_NIKON_TYPE2_NEF_COMPRESSION = 0x0093;
     608   
     609    /**
     610     * The saturation level, as a signed integer.  Examples include:
     611     * <ul>
     612     * <li><code>+3</code></li>
     613     * <li><code>+2</code></li>
     614     * <li><code>+1</code></li>
     615     * <li><code>0</code> Normal</li>
     616     * <li><code>-1</code></li>
     617     * <li><code>-2</code></li>
     618     * <li><code>-3</code> (B&W)</li>
     619     * </ul>
     620     */
     621    public static final int TAG_NIKON_TYPE2_SATURATION = 0x0094;
     622
     623    /**
     624     * The type of noise reduction, as a string.  Examples include:
     625     * <ul>
     626     * <li><code>OFF</code></li>
     627     * <li><code>FPNR</code></li>
     628     * </ul>
     629     */
     630    public static final int TAG_NIKON_TYPE2_NOISE_REDUCTION = 0x0095;
     631    public static final int TAG_NIKON_TYPE2_LINEARIZATION_TABLE = 0x0096;
     632    public static final int TAG_NIKON_TYPE2_COLOR_BALANCE = 0x0097;
     633    public static final int TAG_NIKON_TYPE2_LENS_DATA = 0x0098;
     634
     635    /** The NEF (RAW) thumbnail size, as an integer array with two items representing [width,height]. */
     636    public static final int TAG_NIKON_TYPE2_NEF_THUMBNAIL_SIZE = 0x0099;
     637
     638    /** The sensor pixel size, as a pair of rational numbers. */
     639    public static final int TAG_NIKON_TYPE2_SENSOR_PIXEL_SIZE = 0x009A;
     640    public static final int TAG_NIKON_TYPE2_UNKNOWN_10 = 0x009B;
     641    public static final int TAG_NIKON_TYPE2_SCENE_ASSIST = 0x009C;
     642    public static final int TAG_NIKON_TYPE2_UNKNOWN_11 = 0x009D;
     643    public static final int TAG_NIKON_TYPE2_RETOUCH_HISTORY = 0x009E;
     644    public static final int TAG_NIKON_TYPE2_UNKNOWN_12 = 0x009F;
     645
     646    /**
     647     * The camera serial number, as a string.
     648     * <ul>
     649     * <li><code>NO= 00002539</code></li>
     650     * <li><code>NO= -1000d71</code></li>
     651     * <li><code>PKG597230621263</code></li>
     652     * <li><code>PKG5995671330625116</code></li>
     653     * <li><code>PKG49981281631130677</code></li>
     654     * <li><code>BU672230725063</code></li>
     655     * <li><code>NO= 200332c7</code></li>
     656     * <li><code>NO= 30045efe</code></li>
     657     * </ul>
     658     */
     659    public static final int TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER_2 = 0x00A0;
     660
     661    public static final int TAG_NIKON_TYPE2_IMAGE_DATA_SIZE = 0x00A2;
     662
     663    public static final int TAG_NIKON_TYPE2_UNKNOWN_27 = 0x00A3;
     664    public static final int TAG_NIKON_TYPE2_UNKNOWN_28 = 0x00A4;
     665    public static final int TAG_NIKON_TYPE2_IMAGE_COUNT = 0x00A5;
     666    public static final int TAG_NIKON_TYPE2_DELETED_IMAGE_COUNT = 0x00A6;
     667
     668    /** The number of total shutter releases.  This value increments for each exposure (observed on D70). */
     669    public static final int TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER = 0x00A7;
     670
     671    public static final int TAG_NIKON_TYPE2_FLASH_INFO = 0x00A8;
     672    /**
     673     * The camera's image optimisation, as a string.
     674     * <ul>
     675     *     <li><code></code></li>
     676     *     <li><code>NORMAL</code></li>
     677     *     <li><code>CUSTOM</code></li>
     678     *     <li><code>BLACK AND WHITE</code></li>
     679     *     <li><code>LAND SCAPE</code></li>
     680     *     <li><code>MORE VIVID</code></li>
     681     *     <li><code>PORTRAIT</code></li>
     682     *     <li><code>SOFT</code></li>
     683     *     <li><code>VIVID</code></li>
     684     * </ul>
     685     */
     686    public static final int TAG_NIKON_TYPE2_IMAGE_OPTIMISATION = 0x00A9;
     687
     688    /**
     689     * The camera's saturation level, as a string.
     690     * <ul>
     691     *     <li><code></code></li>
     692     *     <li><code>NORMAL</code></li>
     693     *     <li><code>AUTO</code></li>
     694     *     <li><code>ENHANCED</code></li>
     695     *     <li><code>MODERATE</code></li>
     696     * </ul>
     697     */
     698    public static final int TAG_NIKON_TYPE2_SATURATION_2 = 0x00AA;
     699
     700    /**
     701     * The camera's digital vari-program setting, as a string.
     702     * <ul>
     703     *     <li><code></code></li>
     704     *     <li><code>AUTO</code></li>
     705     *     <li><code>AUTO(FLASH OFF)</code></li>
     706     *     <li><code>CLOSE UP</code></li>
     707     *     <li><code>LANDSCAPE</code></li>
     708     *     <li><code>NIGHT PORTRAIT</code></li>
     709     *     <li><code>PORTRAIT</code></li>
     710     *     <li><code>SPORT</code></li>
     711     * </ul>
     712     */
     713    public static final int TAG_NIKON_TYPE2_DIGITAL_VARI_PROGRAM = 0x00AB;
     714
     715    /**
     716     * The camera's digital vari-program setting, as a string.
     717     * <ul>
     718     *     <li><code></code></li>
     719     *     <li><code>VR-ON</code></li>
     720     *     <li><code>VR-OFF</code></li>
     721     *     <li><code>VR-HYBRID</code></li>
     722     *     <li><code>VR-ACTIVE</code></li>
     723     * </ul>
     724     */
     725    public static final int TAG_NIKON_TYPE2_IMAGE_STABILISATION = 0x00AC;
     726
     727    /**
     728     * The camera's digital vari-program setting, as a string.
     729     * <ul>
     730     *     <li><code></code></li>
     731     *     <li><code>HYBRID</code></li>
     732     *     <li><code>STANDARD</code></li>
     733     * </ul>
     734     */
     735    public static final int TAG_NIKON_TYPE2_AF_RESPONSE = 0x00AD;
     736    public static final int TAG_NIKON_TYPE2_UNKNOWN_29 = 0x00AE;
     737    public static final int TAG_NIKON_TYPE2_UNKNOWN_30 = 0x00AF;
     738    public static final int TAG_NIKON_TYPE2_MULTI_EXPOSURE = 0x00B0;
     739
     740    /**
     741     * The camera's high ISO noise reduction setting, as an integer.
     742     * <ul>
     743     *     <li><code>0</code> Off</li>
     744     *     <li><code>1</code> Minimal</li>
     745     *     <li><code>2</code> Low</li>
     746     *     <li><code>4</code> Normal</li>
     747     *     <li><code>6</code> High</li>
     748     * </ul>
     749     */
     750    public static final int TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION = 0x00B1;
     751    public static final int TAG_NIKON_TYPE2_UNKNOWN_31 = 0x00B2;
     752    public static final int TAG_NIKON_TYPE2_UNKNOWN_32 = 0x00B3;
     753    public static final int TAG_NIKON_TYPE2_UNKNOWN_33 = 0x00B4;
     754    public static final int TAG_NIKON_TYPE2_UNKNOWN_48 = 0x00B5;
     755    public static final int TAG_NIKON_TYPE2_POWER_UP_TIME = 0x00B6;
     756    public static final int TAG_NIKON_TYPE2_AF_INFO_2 = 0x00B7;
     757    public static final int TAG_NIKON_TYPE2_FILE_INFO = 0x00B8;
     758    public static final int TAG_NIKON_TYPE2_AF_TUNE = 0x00B9;
     759    public static final int TAG_NIKON_TYPE2_UNKNOWN_49 = 0x00BB;
     760    public static final int TAG_NIKON_TYPE2_UNKNOWN_50 = 0x00BD;
     761    public static final int TAG_NIKON_TYPE2_UNKNOWN_51 = 0x0103;
     762    public static final int TAG_NIKON_TYPE2_PRINT_IM = 0x0E00;
     763
     764    /**
     765     * Data about changes set by Nikon Capture Editor.
     766     *
     767     * Values observed
     768     */
     769    public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_DATA = 0x0E01;
     770    public static final int TAG_NIKON_TYPE2_UNKNOWN_52 = 0x0E05;
     771    public static final int TAG_NIKON_TYPE2_UNKNOWN_53 = 0x0E08;
     772    public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_VERSION = 0x0E09;
     773    public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_OFFSETS = 0x0E0E;
     774    public static final int TAG_NIKON_TYPE2_NIKON_SCAN = 0x0E10;
     775    public static final int TAG_NIKON_TYPE2_UNKNOWN_54 = 0x0E19;
     776    public static final int TAG_NIKON_TYPE2_NEF_BIT_DEPTH = 0x0E22;
     777    public static final int TAG_NIKON_TYPE2_UNKNOWN_55 = 0x0E23;
     778
     779    @NotNull
     780    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     781
     782    static
     783    {
     784        _tagNameMap.put(TAG_NIKON_TYPE2_FIRMWARE_VERSION, "Firmware Version");
     785        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_1, "ISO");
     786        _tagNameMap.put(TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT, "Quality & File Format");
     787        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE, "White Balance");
     788        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SHARPENING, "Sharpening");
     789        _tagNameMap.put(TAG_NIKON_TYPE2_AF_TYPE, "AF Type");
     790        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE, "White Balance Fine");
     791        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF, "White Balance RB Coefficients");
     792        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_REQUESTED, "ISO");
     793        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_MODE, "ISO Mode");
     794        _tagNameMap.put(TAG_NIKON_TYPE2_DATA_DUMP, "Data Dump");
     795
     796        _tagNameMap.put(TAG_NIKON_TYPE2_PROGRAM_SHIFT, "Program Shift");
     797        _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE, "Exposure Difference");
     798        _tagNameMap.put(TAG_NIKON_TYPE2_PREVIEW_IFD, "Preview IFD");
     799        _tagNameMap.put(TAG_NIKON_TYPE2_LENS_TYPE, "Lens Type");
     800        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_USED, "Flash Used");
     801        _tagNameMap.put(TAG_NIKON_TYPE2_AF_FOCUS_POSITION, "AF Focus Position");
     802        _tagNameMap.put(TAG_NIKON_TYPE2_SHOOTING_MODE, "Shooting Mode");
     803        _tagNameMap.put(TAG_NIKON_TYPE2_LENS_STOPS, "Lens Stops");
     804        _tagNameMap.put(TAG_NIKON_TYPE2_CONTRAST_CURVE, "Contrast Curve");
     805        _tagNameMap.put(TAG_NIKON_TYPE2_LIGHT_SOURCE, "Light source");
     806        _tagNameMap.put(TAG_NIKON_TYPE2_SHOT_INFO, "Shot Info");
     807        _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_BALANCE, "Color Balance");
     808        _tagNameMap.put(TAG_NIKON_TYPE2_LENS_DATA, "Lens Data");
     809        _tagNameMap.put(TAG_NIKON_TYPE2_NEF_THUMBNAIL_SIZE, "NEF Thumbnail Size");
     810        _tagNameMap.put(TAG_NIKON_TYPE2_SENSOR_PIXEL_SIZE, "Sensor Pixel Size");
     811        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_10, "Unknown 10");
     812        _tagNameMap.put(TAG_NIKON_TYPE2_SCENE_ASSIST, "Scene Assist");
     813        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_11, "Unknown 11");
     814        _tagNameMap.put(TAG_NIKON_TYPE2_RETOUCH_HISTORY, "Retouch History");
     815        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_12, "Unknown 12");
     816        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_SYNC_MODE, "Flash Sync Mode");
     817        _tagNameMap.put(TAG_NIKON_TYPE2_AUTO_FLASH_MODE, "Auto Flash Mode");
     818        _tagNameMap.put(TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION, "Auto Flash Compensation");
     819        _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER, "Exposure Sequence Number");
     820        _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_MODE, "Color Mode");
     821
     822        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_20, "Unknown 20");
     823        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_BOUNDARY, "Image Boundary");
     824        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION, "Flash Exposure Compensation");
     825        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION, "Flash Bracket Compensation");
     826        _tagNameMap.put(TAG_NIKON_TYPE2_AE_BRACKET_COMPENSATION, "AE Bracket Compensation");
     827        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_MODE, "Flash Mode");
     828        _tagNameMap.put(TAG_NIKON_TYPE2_CROP_HIGH_SPEED, "Crop High Speed");
     829        _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_TUNING, "Exposure Tuning");
     830        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER, "Camera Serial Number");
     831        _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_SPACE, "Color Space");
     832        _tagNameMap.put(TAG_NIKON_TYPE2_VR_INFO, "VR Info");
     833        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_AUTHENTICATION, "Image Authentication");
     834        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_35, "Unknown 35");
     835        _tagNameMap.put(TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING, "Active D-Lighting");
     836        _tagNameMap.put(TAG_NIKON_TYPE2_PICTURE_CONTROL, "Picture Control");
     837        _tagNameMap.put(TAG_NIKON_TYPE2_WORLD_TIME, "World Time");
     838        _tagNameMap.put(TAG_NIKON_TYPE2_ISO_INFO, "ISO Info");
     839        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_36, "Unknown 36");
     840        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_37, "Unknown 37");
     841        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_38, "Unknown 38");
     842        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_39, "Unknown 39");
     843        _tagNameMap.put(TAG_NIKON_TYPE2_VIGNETTE_CONTROL, "Vignette Control");
     844        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_40, "Unknown 40");
     845        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_41, "Unknown 41");
     846        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_42, "Unknown 42");
     847        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_43, "Unknown 43");
     848        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_44, "Unknown 44");
     849        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_45, "Unknown 45");
     850        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_46, "Unknown 46");
     851        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_47, "Unknown 47");
     852        _tagNameMap.put(TAG_NIKON_TYPE2_SCENE_MODE, "Scene Mode");
     853
     854        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER_2, "Camera Serial Number");
     855        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_DATA_SIZE, "Image Data Size");
     856        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_27, "Unknown 27");
     857        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_28, "Unknown 28");
     858        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_COUNT, "Image Count");
     859        _tagNameMap.put(TAG_NIKON_TYPE2_DELETED_IMAGE_COUNT, "Deleted Image Count");
     860        _tagNameMap.put(TAG_NIKON_TYPE2_SATURATION_2, "Saturation");
     861        _tagNameMap.put(TAG_NIKON_TYPE2_DIGITAL_VARI_PROGRAM, "Digital Vari Program");
     862        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_STABILISATION, "Image Stabilisation");
     863        _tagNameMap.put(TAG_NIKON_TYPE2_AF_RESPONSE, "AF Response");
     864        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_29, "Unknown 29");
     865        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_30, "Unknown 30");
     866        _tagNameMap.put(TAG_NIKON_TYPE2_MULTI_EXPOSURE, "Multi Exposure");
     867        _tagNameMap.put(TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION, "High ISO Noise Reduction");
     868        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_31, "Unknown 31");
     869        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_32, "Unknown 32");
     870        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_33, "Unknown 33");
     871        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_48, "Unknown 48");
     872        _tagNameMap.put(TAG_NIKON_TYPE2_POWER_UP_TIME, "Power Up Time");
     873        _tagNameMap.put(TAG_NIKON_TYPE2_AF_INFO_2, "AF Info 2");
     874        _tagNameMap.put(TAG_NIKON_TYPE2_FILE_INFO, "File Info");
     875        _tagNameMap.put(TAG_NIKON_TYPE2_AF_TUNE, "AF Tune");
     876        _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_INFO, "Flash Info");
     877        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_OPTIMISATION, "Image Optimisation");
     878
     879        _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT, "Image Adjustment");
     880        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION, "Tone Compensation");
     881        _tagNameMap.put(TAG_NIKON_TYPE2_ADAPTER, "Adapter");
     882        _tagNameMap.put(TAG_NIKON_TYPE2_LENS, "Lens");
     883        _tagNameMap.put(TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE, "Manual Focus Distance");
     884        _tagNameMap.put(TAG_NIKON_TYPE2_DIGITAL_ZOOM, "Digital Zoom");
     885        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_COLOR_MODE, "Colour Mode");
     886        _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT, "Camera Hue Adjustment");
     887        _tagNameMap.put(TAG_NIKON_TYPE2_NEF_COMPRESSION, "NEF Compression");
     888        _tagNameMap.put(TAG_NIKON_TYPE2_SATURATION, "Saturation");
     889        _tagNameMap.put(TAG_NIKON_TYPE2_NOISE_REDUCTION, "Noise Reduction");
     890        _tagNameMap.put(TAG_NIKON_TYPE2_LINEARIZATION_TABLE, "Linearization Table");
     891        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_DATA, "Nikon Capture Data");
     892        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_49, "Unknown 49");
     893        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_50, "Unknown 50");
     894        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_51, "Unknown 51");
     895        _tagNameMap.put(TAG_NIKON_TYPE2_PRINT_IM, "Print IM");
     896        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_52, "Unknown 52");
     897        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_53, "Unknown 53");
     898        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_VERSION, "Nikon Capture Version");
     899        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_OFFSETS, "Nikon Capture Offsets");
     900        _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_SCAN, "Nikon Scan");
     901        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_54, "Unknown 54");
     902        _tagNameMap.put(TAG_NIKON_TYPE2_NEF_BIT_DEPTH, "NEF Bit Depth");
     903        _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_55, "Unknown 55");
     904    }
     905
     906    public NikonType2MakernoteDirectory()
     907    {
     908        this.setDescriptor(new NikonType2MakernoteDescriptor(this));
     909    }
     910
     911    @NotNull
     912    public String getName()
     913    {
     914        return "Nikon Makernote";
     915    }
     916
     917    @NotNull
     918    protected HashMap<Integer, String> getTagNameMap()
     919    {
     920        return _tagNameMap;
     921    }
     922}
  • src/com/drew/metadata/exif/OlympusMakernoteDescriptor.java

     
    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) {
    3443            case OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE:
     
    4049            case OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO:
    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:
    5364                return "Normal";
     
    5869        }
    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:
    6780                return "Normal (no macro)";
     
    7285        }
    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:
    8196                return "SQ";
     
    88103        }
    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:
    98117                desc.append("Normal picture taking mode");
     
    110129                desc.append("Unknown picture taking mode");
    111130                break;
    112131        }
     132
     133        if (values.length < 2)
     134            return desc.toString();
    113135        desc.append(" - ");
    114136        switch (values[1]) {
    115137            case 0:
    116138                desc.append("Unknown sequence number");
    117139                break;
    118140            case 1:
    119                 desc.append("1st in a sequnce");
     141                desc.append("1st in a sequence");
    120142                break;
    121143            case 2:
    122144                desc.append("2nd in a sequence");
     
    129151                desc.append("th in a sequence");
    130152                break;
    131153        }
     154        if (values.length < 3)
     155            return desc.toString();
     156        desc.append(" - ");
    132157        switch (values[2]) {
    133158            case 1:
    134159                desc.append("Left to right panorama direction");
  • src/com/drew/metadata/exif/OlympusMakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    6451    /**
     
    8370
    8471    /**
    8572     * Not 100% sure about this tag.
    86      *
     73     * <p/>
    8774     * Used by Konica / Minolta cameras.
    8875     * 0 = Raw
    8976     * 1 = Super Fine
     
    118105     */
    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
    156118    /**
     
    165127     */
    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
    173133    /**
     
    176136     */
    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();
     163    @NotNull
     164    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    295165
    296     static
    297     {
    298         tagNameMap.put(new Integer(TAG_OLYMPUS_SPECIAL_MODE), "Special Mode");
    299         tagNameMap.put(new Integer(TAG_OLYMPUS_JPEG_QUALITY), "Jpeg Quality");
    300         tagNameMap.put(new Integer(TAG_OLYMPUS_MACRO_MODE), "Macro");
    301         tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_1), "Makernote Unknown 1");
    302         tagNameMap.put(new Integer(TAG_OLYMPUS_DIGI_ZOOM_RATIO), "DigiZoom Ratio");
    303         tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_2), "Makernote Unknown 2");
    304         tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_3), "Makernote Unknown 3");
    305         tagNameMap.put(new Integer(TAG_OLYMPUS_FIRMWARE_VERSION), "Firmware Version");
    306         tagNameMap.put(new Integer(TAG_OLYMPUS_PICT_INFO), "Pict Info");
    307         tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_ID), "Camera Id");
    308         tagNameMap.put(new Integer(TAG_OLYMPUS_DATA_DUMP), "Data Dump");
    309         tagNameMap.put(new Integer(TAG_OLYMPUS_MAKERNOTE_VERSION), "Makernote Version");
    310         tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_SETTINGS_1), "Camera Settings");
    311         tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_SETTINGS_2), "Camera Settings");
    312         tagNameMap.put(new Integer(TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE), "Compressed Image Size");
    313         tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1), "Thumbnail Offset");
    314         tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2), "Thumbnail Offset");
    315         tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH), "Thumbnail Length");
    316         tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_MODE), "Colour Mode");
    317         tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_QUALITY_1), "Image Quality");
    318         tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_QUALITY_2), "Image Quality");
    319         tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_HEIGHT), "Image Height");
    320         tagNameMap.put(new Integer(TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL), "Original Manufacturer Model");
    321         tagNameMap.put(new Integer(TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");
    322         tagNameMap.put(new Integer(TAG_OLYMPUS_FLASH_MODE), "Flash Mode");
    323         tagNameMap.put(new Integer(TAG_OLYMPUS_BRACKET), "Bracket");
    324         tagNameMap.put(new Integer(TAG_OLYMPUS_FOCUS_MODE), "Focus Mode");
    325         tagNameMap.put(new Integer(TAG_OLYMPUS_FOCUS_DISTANCE), "Focus Distance");
    326         tagNameMap.put(new Integer(TAG_OLYMPUS_ZOOM), "Zoom");
    327         tagNameMap.put(new Integer(TAG_OLYMPUS_MACRO_FOCUS), "Macro Focus");
    328         tagNameMap.put(new Integer(TAG_OLYMPUS_SHARPNESS), "Sharpness");
    329         tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_MATRIX), "Colour Matrix");
    330         tagNameMap.put(new Integer(TAG_OLYMPUS_BLACK_LEVEL), "Black Level");
    331         tagNameMap.put(new Integer(TAG_OLYMPUS_WHITE_BALANCE), "White Balance");
    332         tagNameMap.put(new Integer(TAG_OLYMPUS_RED_BIAS), "Red Bias");
    333         tagNameMap.put(new Integer(TAG_OLYMPUS_BLUE_BIAS), "Blue Bias");
    334         tagNameMap.put(new Integer(TAG_OLYMPUS_SERIAL_NUMBER), "Serial Number");
    335         tagNameMap.put(new Integer(TAG_OLYMPUS_FLASH_BIAS), "Flash Bias");
    336         tagNameMap.put(new Integer(TAG_OLYMPUS_CONTRAST), "Contrast");
    337         tagNameMap.put(new Integer(TAG_OLYMPUS_SHARPNESS_FACTOR), "Sharpness Factor");
    338         tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_CONTROL), "Colour Control");
    339         tagNameMap.put(new Integer(TAG_OLYMPUS_VALID_BITS), "Valid Bits");
    340         tagNameMap.put(new Integer(TAG_OLYMPUS_CORING_FILTER), "Coring Filter");
    341         tagNameMap.put(new Integer(TAG_OLYMPUS_FINAL_WIDTH), "Final Width");
    342         tagNameMap.put(new Integer(TAG_OLYMPUS_FINAL_HEIGHT), "Final Height");
    343         tagNameMap.put(new Integer(TAG_OLYMPUS_COMPRESSION_RATIO), "Compression Ratio");
     166    static {
     167        _tagNameMap.put(TAG_OLYMPUS_SPECIAL_MODE, "Special Mode");
     168        _tagNameMap.put(TAG_OLYMPUS_JPEG_QUALITY, "Jpeg Quality");
     169        _tagNameMap.put(TAG_OLYMPUS_MACRO_MODE, "Macro");
     170        _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_1, "Makernote Unknown 1");
     171        _tagNameMap.put(TAG_OLYMPUS_DIGI_ZOOM_RATIO, "DigiZoom Ratio");
     172        _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_2, "Makernote Unknown 2");
     173        _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_3, "Makernote Unknown 3");
     174        _tagNameMap.put(TAG_OLYMPUS_FIRMWARE_VERSION, "Firmware Version");
     175        _tagNameMap.put(TAG_OLYMPUS_PICT_INFO, "Pict Info");
     176        _tagNameMap.put(TAG_OLYMPUS_CAMERA_ID, "Camera Id");
     177        _tagNameMap.put(TAG_OLYMPUS_DATA_DUMP, "Data Dump");
     178        _tagNameMap.put(TAG_OLYMPUS_MAKERNOTE_VERSION, "Makernote Version");
     179        _tagNameMap.put(TAG_OLYMPUS_CAMERA_SETTINGS_1, "Camera Settings");
     180        _tagNameMap.put(TAG_OLYMPUS_CAMERA_SETTINGS_2, "Camera Settings");
     181        _tagNameMap.put(TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE, "Compressed Image Size");
     182        _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1, "Thumbnail Offset");
     183        _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2, "Thumbnail Offset");
     184        _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH, "Thumbnail Length");
     185        _tagNameMap.put(TAG_OLYMPUS_COLOUR_MODE, "Colour Mode");
     186        _tagNameMap.put(TAG_OLYMPUS_IMAGE_QUALITY_1, "Image Quality");
     187        _tagNameMap.put(TAG_OLYMPUS_IMAGE_QUALITY_2, "Image Quality");
     188        _tagNameMap.put(TAG_OLYMPUS_IMAGE_HEIGHT, "Image Height");
     189        _tagNameMap.put(TAG_OLYMPUS_IMAGE_WIDTH, "Image Width");
     190        _tagNameMap.put(TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL, "Original Manufacturer Model");
     191        _tagNameMap.put(TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
     192        _tagNameMap.put(TAG_OLYMPUS_FLASH_MODE, "Flash Mode");
     193        _tagNameMap.put(TAG_OLYMPUS_BRACKET, "Bracket");
     194        _tagNameMap.put(TAG_OLYMPUS_FOCUS_MODE, "Focus Mode");
     195        _tagNameMap.put(TAG_OLYMPUS_FOCUS_DISTANCE, "Focus Distance");
     196        _tagNameMap.put(TAG_OLYMPUS_ZOOM, "Zoom");
     197        _tagNameMap.put(TAG_OLYMPUS_MACRO_FOCUS, "Macro Focus");
     198        _tagNameMap.put(TAG_OLYMPUS_SHARPNESS, "Sharpness");
     199        _tagNameMap.put(TAG_OLYMPUS_COLOUR_MATRIX, "Colour Matrix");
     200        _tagNameMap.put(TAG_OLYMPUS_BLACK_LEVEL, "Black Level");
     201        _tagNameMap.put(TAG_OLYMPUS_WHITE_BALANCE, "White Balance");
     202        _tagNameMap.put(TAG_OLYMPUS_RED_BIAS, "Red Bias");
     203        _tagNameMap.put(TAG_OLYMPUS_BLUE_BIAS, "Blue Bias");
     204        _tagNameMap.put(TAG_OLYMPUS_SERIAL_NUMBER, "Serial Number");
     205        _tagNameMap.put(TAG_OLYMPUS_FLASH_BIAS, "Flash Bias");
     206        _tagNameMap.put(TAG_OLYMPUS_CONTRAST, "Contrast");
     207        _tagNameMap.put(TAG_OLYMPUS_SHARPNESS_FACTOR, "Sharpness Factor");
     208        _tagNameMap.put(TAG_OLYMPUS_COLOUR_CONTROL, "Colour Control");
     209        _tagNameMap.put(TAG_OLYMPUS_VALID_BITS, "Valid Bits");
     210        _tagNameMap.put(TAG_OLYMPUS_CORING_FILTER, "Coring Filter");
     211        _tagNameMap.put(TAG_OLYMPUS_FINAL_WIDTH, "Final Width");
     212        _tagNameMap.put(TAG_OLYMPUS_FINAL_HEIGHT, "Final Height");
     213        _tagNameMap.put(TAG_OLYMPUS_COMPRESSION_RATIO, "Compression Ratio");
    344214    }
    345215
    346216    public OlympusMakernoteDirectory()
     
    348218        this.setDescriptor(new OlympusMakernoteDescriptor(this));
    349219    }
    350220
     221    @NotNull
    351222    public String getName()
    352223    {
    353224        return "Olympus Makernote";
    354225    }
    355226
    356     protected HashMap getTagNameMap()
     227    @NotNull
     228    protected HashMap<Integer, String> getTagNameMap()
    357229    {
    358         return tagNameMap;
     230        return _tagNameMap;
    359231    }
    360232}
  • src/com/drew/metadata/exif/PanasonicMakernoteDescriptor.java

     
    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
     52    @Nullable
     53    public String getDescription(int tagType)
    3554    {
    36         switch (tagType)
    37         {
    38             case PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE:
     55        switch (tagType) {
     56            case PanasonicMakernoteDirectory.TAG_QUALITY_MODE:
     57                return getQualityModeDescription();
     58            case PanasonicMakernoteDirectory.TAG_VERSION:
     59                return getVersionDescription();
     60            case PanasonicMakernoteDirectory.TAG_WHITE_BALANCE:
     61                return getWhiteBalanceDescription();
     62            case PanasonicMakernoteDirectory.TAG_FOCUS_MODE:
     63                return getFocusModeDescription();
     64            case PanasonicMakernoteDirectory.TAG_AF_AREA_MODE:
     65                return getAfAreaModeDescription();
     66            case PanasonicMakernoteDirectory.TAG_IMAGE_STABILIZATION:
     67                return getImageStabilizationDescription();
     68            case PanasonicMakernoteDirectory.TAG_MACRO_MODE:
    3969                return getMacroModeDescription();
    40             case PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE:
     70            case PanasonicMakernoteDirectory.TAG_RECORD_MODE:
    4171                return getRecordModeDescription();
    42             case PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO:
     72            case PanasonicMakernoteDirectory.TAG_AUDIO:
     73                return getAudioDescription();
     74            case PanasonicMakernoteDirectory.TAG_UNKNOWN_DATA_DUMP:
     75                return getUnknownDataDumpDescription();
     76            case PanasonicMakernoteDirectory.TAG_COLOR_EFFECT:
     77                return getColorEffectDescription();
     78            case PanasonicMakernoteDirectory.TAG_UPTIME:
     79                return getUptimeDescription();
     80            case PanasonicMakernoteDirectory.TAG_BURST_MODE:
     81                return getBurstModeDescription();
     82            case PanasonicMakernoteDirectory.TAG_CONTRAST_MODE:
     83                return getContrastModeDescription();
     84            case PanasonicMakernoteDirectory.TAG_NOISE_REDUCTION:
     85                return getNoiseReductionDescription();
     86            case PanasonicMakernoteDirectory.TAG_SELF_TIMER:
     87                return getSelfTimerDescription();
     88            case PanasonicMakernoteDirectory.TAG_ROTATION:
     89                return getRotationDescription();
     90            case PanasonicMakernoteDirectory.TAG_AF_ASSIST_LAMP:
     91                return getAfAssistLampDescription();
     92            case PanasonicMakernoteDirectory.TAG_COLOR_MODE:
     93                return getColorModeDescription();
     94            case PanasonicMakernoteDirectory.TAG_OPTICAL_ZOOM_MODE:
     95                return getOpticalZoomModeDescription();
     96            case PanasonicMakernoteDirectory.TAG_CONVERSION_LENS:
     97                return getConversionLensDescription();
     98            case PanasonicMakernoteDirectory.TAG_CONTRAST:
     99                return getContrastDescription();
     100            case PanasonicMakernoteDirectory.TAG_WORLD_TIME_LOCATION:
     101                return getWorldTimeLocationDescription();
     102            case PanasonicMakernoteDirectory.TAG_ADVANCED_SCENE_MODE:
     103                return getAdvancedSceneModeDescription();
     104            case PanasonicMakernoteDirectory.TAG_FACE_DETECTION_INFO:
     105                return getDetectedFacesDescription();
     106            case PanasonicMakernoteDirectory.TAG_TRANSFORM:
     107                return getTransformDescription();
     108                        case PanasonicMakernoteDirectory.TAG_TRANSFORM_1:
     109                    return getTransform1Description();
     110            case PanasonicMakernoteDirectory.TAG_INTELLIGENT_EXPOSURE:
     111                return getIntelligentExposureDescription();
     112            case PanasonicMakernoteDirectory.TAG_FLASH_WARNING:
     113                return getFlashWarningDescription();
     114            case PanasonicMakernoteDirectory.TAG_COUNTRY:
     115                return getCountryDescription();
     116            case PanasonicMakernoteDirectory.TAG_STATE:
     117                return getStateDescription();
     118            case PanasonicMakernoteDirectory.TAG_CITY:
     119                return getCityDescription();
     120            case PanasonicMakernoteDirectory.TAG_LANDMARK:
     121                return getLandmarkDescription();
     122            case PanasonicMakernoteDirectory.TAG_INTELLIGENT_RESOLUTION:
     123                return getIntelligentResolutionDescription();
     124            case PanasonicMakernoteDirectory.TAG_FACE_RECOGNITION_INFO:
     125                return getRecognizedFacesDescription();
     126            case PanasonicMakernoteDirectory.TAG_PRINT_IMAGE_MATCHING_INFO:
    43127                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";
    44219            default:
    45                 return _directory.getString(tagType);
     220                return "Unknown (" + value + ")";
    46221        }
    47222    }
    48223
    49     public String getPrintImageMatchingInfoDescription() throws MetadataException
     224    @Nullable
     225    public String getAudioDescription()
    50226    {
    51         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null;
    52         byte[] bytes = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO);
    53         return "(" + bytes.length + " bytes)";
     227        return getOnOffDescription(PanasonicMakernoteDirectory.TAG_AUDIO);
    54228    }
    55229
    56     public String getMacroModeDescription() throws MetadataException
     230    @Nullable
     231    public String getTransformDescription()
    57232    {
    58         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null;
    59         int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     233        return getTransformDescription(PanasonicMakernoteDirectory.TAG_TRANSFORM);
     234    }
     235
     236    @Nullable
     237    public String getTransform1Description()
     238    {
     239        return getTransformDescription(PanasonicMakernoteDirectory.TAG_TRANSFORM_1);
     240    }
     241
     242    @Nullable
     243    private String getTransformDescription(int tag)
     244    {
     245        byte[] values = _directory.getByteArray(tag);
     246        if (values == null)
     247            return null;
     248
     249        BufferReader reader = new ByteArrayReader(values);
     250
     251        try
     252        {
     253            int val1 = reader.getUInt16(0);
     254            int val2 = reader.getUInt16(2);
     255
     256            if (val1 == -1 && val2 == 1)
     257                return "Slim Low";
     258            if (val1 == -3 && val2 == 2)
     259                return "Slim High";
     260            if (val1 == 0 && val2 == 0)
     261                return "Off";
     262            if (val1 == 1 && val2 == 1)
     263                return "Stretch Low";
     264            if (val1 == 3 && val2 == 2)
     265                return "Stretch High";
     266
     267            return "Unknown (" + val1 + " " + val2 + ")";
     268        } catch (BufferBoundsException e) {
     269            return null   ;
     270        }
     271    }
     272
     273    @Nullable
     274    public String getIntelligentExposureDescription()
     275    {
     276        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_INTELLIGENT_EXPOSURE);
     277        if (value == null)
     278            return null;
    60279        switch (value) {
     280            case 0:
     281                return "Off";
    61282            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";
     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";
    63393            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";
     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";
    65462            default:
    66463                return "Unknown (" + value + ")";
    67464        }
    68465    }
    69466
    70     public String getRecordModeDescription() throws MetadataException
     467    @Nullable
     468    public String getUptimeDescription()
    71469    {
    72         if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null;
    73         int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     470        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_UPTIME);
     471        if (value == null)
     472            return null;
     473        return value / 100f + " s";
     474    }
     475
     476    @Nullable
     477    public String getBurstModeDescription()
     478    {
     479        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_BURST_MODE);
     480        if (value == null)
     481            return null;
    74482        switch (value) {
     483            case 0:
     484                return "Off";
    75485            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)";
    77536            case 2:
     537                return "High (+1)";
     538            case 3:
     539                return "Lowest (-2)";
     540            case 4:
     541                return "Highest (+2)";
     542            default:
     543                return "Unknown (" + value + ")";
     544        }
     545    }
     546
     547
     548    @Nullable
     549    public String getSelfTimerDescription()
     550    {
     551        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_SELF_TIMER);
     552        if (value == null)
     553            return null;
     554        switch (value) {
     555            case 1:
     556                return "Off";
     557            case 2:
     558                return "10 s";
     559            case 3:
     560                return "2 s";
     561            default:
     562                return "Unknown (" + value + ")";
     563        }
     564    }
     565
     566    @Nullable
     567    public String getRotationDescription()
     568    {
     569        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_ROTATION);
     570        if (value == null)
     571            return null;
     572        switch (value) {
     573            case 1:
     574                return "Horizontal";
     575            case 3:
     576                return "Rotate 180";
     577            case 6:
     578                return "Rotate 90 CW";
     579            case 8:
     580                return "Rotate 270 CW";
     581            default:
     582                return "Unknown (" + value + ")";
     583        }
     584    }
     585
     586    @Nullable
     587    public String getAfAssistLampDescription()
     588    {
     589        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_AF_ASSIST_LAMP);
     590        if (value == null)
     591            return null;
     592        switch (value) {
     593            case 1:
     594                return "Fired";
     595            case 2:
     596                return "Enabled but not used";
     597            case 3:
     598                return "Disabled but required";
     599            case 4:
     600                return "Disabled and not required";
     601            default:
     602                return "Unknown (" + value + ")";
     603        }
     604    }
     605
     606    @Nullable
     607    public String getColorModeDescription()
     608    {
     609        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_COLOR_MODE);
     610        if (value == null)
     611            return null;
     612        switch (value) {
     613            case 0:
     614                return "Normal";
     615            case 1:
     616                return "Natural";
     617            case 2:
     618                return "Vivid";
     619            default:
     620                return "Unknown (" + value + ")";
     621        }
     622    }
     623
     624    @Nullable
     625    public String getOpticalZoomModeDescription()
     626    {
     627        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_OPTICAL_ZOOM_MODE);
     628        if (value == null)
     629            return null;
     630        switch (value) {
     631            case 1:
     632                return "Standard";
     633            case 2:
     634                return "Extended";
     635            default:
     636                return "Unknown (" + value + ")";
     637        }
     638    }
     639
     640    @Nullable
     641    public String getConversionLensDescription()
     642    {
     643        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONVERSION_LENS);
     644        if (value == null)
     645            return null;
     646        switch (value) {
     647            case 1:
     648                return "Off";
     649            case 2:
     650                return "Wide";
     651            case 3:
     652                return "Telephoto";
     653            case 4:
     654                return "Macro";
     655            default:
     656                return "Unknown (" + value + ")";
     657        }
     658    }
     659
     660    @Nullable
     661    public String getDetectedFacesDescription()
     662    {
     663        return buildFacesDescription(_directory.getDetectedFaces());
     664    }
     665
     666    @Nullable
     667    public String getRecognizedFacesDescription()
     668    {
     669        return buildFacesDescription(_directory.getRecognizedFaces());
     670    }
     671
     672    @Nullable
     673    private String buildFacesDescription(@Nullable Face[] faces)
     674    {
     675        if (faces == null)
     676            return null;
     677
     678        StringBuilder result = new StringBuilder();
     679
     680        for (int i = 0; i < faces.length; i++)
     681            result.append("Face ").append(i + 1).append(": ").append(faces[i].toString()).append("\n");
     682
     683        if (result.length() > 0)
     684            return result.substring(0, result.length() - 1);
     685
     686        return null;
     687    }
     688
     689    @Nullable
     690    public String getRecordModeDescription()
     691    {
     692        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_RECORD_MODE);
     693        if (value == null)
     694            return null;
     695        switch (value) {
     696            case 1:
     697                return "Normal";
     698            case 2:
    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";
    81786            default:
    82787                return "Unknown (" + value + ")";
    83788        }
    84789    }
     790
     791    @Nullable
     792    public String getSceneModeDescription()
     793    {
     794        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_SCENE_MODE);
     795        if (value == null)
     796            return null;
     797        switch (value) {
     798            case 1:
     799                return "Normal";
     800            case 2:
     801                return "Portrait";
     802            case 3:
     803                return "Scenery";
     804            case 4:
     805                return "Sports";
     806            case 5:
     807                return "Night Portrait";
     808            case 6:
     809                return "Program";
     810            case 7:
     811                return "Aperture Priority";
     812            case 8:
     813                return "Shutter Priority";
     814            case 9:
     815                return "Macro";
     816            case 10:
     817                return "Spot";
     818            case 11:
     819                return "Manual";
     820            case 12:
     821                return "Movie Preview";
     822            case 13:
     823                return "Panning";
     824            case 14:
     825                return "Simple";
     826            case 15:
     827                return "Color Effects";
     828            case 16:
     829                return "Self Portrait";
     830            case 17:
     831                return "Economy";
     832            case 18:
     833                return "Fireworks";
     834            case 19:
     835                return "Party";
     836            case 20:
     837                return "Snow";
     838            case 21:
     839                return "Night Scenery";
     840            case 22:
     841                return "Food";
     842            case 23:
     843                return "Baby";
     844            case 24:
     845                return "Soft Skin";
     846            case 25:
     847                return "Candlelight";
     848            case 26:
     849                return "Starry Night";
     850            case 27:
     851                return "High Sensitivity";
     852            case 28:
     853                return "Panorama Assist";
     854            case 29:
     855                return "Underwater";
     856            case 30:
     857                return "Beach";
     858            case 31:
     859                return "Aerial Photo";
     860            case 32:
     861                return "Sunset";
     862            case 33:
     863                return "Pet";
     864            case 34:
     865                return "Intelligent ISO";
     866            case 35:
     867                return "Clipboard";
     868            case 36:
     869                return "High Speed Continuous Shooting";
     870            case 37:
     871                return "Intelligent Auto";
     872            case 39:
     873                return "Multi-aspect";
     874            case 41:
     875                return "Transform";
     876            case 42:
     877                return "Flash Burst";
     878            case 43:
     879                return "Pin Hole";
     880            case 44:
     881                return "Film Grain";
     882            case 45:
     883                return "My Color";
     884            case 46:
     885                return "Photo Frame";
     886            case 51:
     887                return "HDR";
     888            default:
     889                return "Unknown (" + value + ")";
     890        }
     891    }
     892
     893    @Nullable
     894    public String getFocusModeDescription()
     895    {
     896        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_FOCUS_MODE);
     897        if (value == null)
     898            return null;
     899        switch (value) {
     900            case 1:
     901                return "Auto";
     902            case 2:
     903                return "Manual";
     904            case 4:
     905                return "Auto, Focus Button";
     906            case 5:
     907                return "Auto, Continuous";
     908            default:
     909                return "Unknown (" + value + ")";
     910        }
     911    }
     912
     913    @Nullable
     914    public String getAfAreaModeDescription()
     915    {
     916        int[] value = _directory.getIntArray(PanasonicMakernoteDirectory.TAG_AF_AREA_MODE);
     917        if (value == null || value.length < 2)
     918            return null;
     919        switch (value[0]) {
     920            case 0:
     921                switch (value[1]) {
     922                    case 1:
     923                        return "Spot Mode On";
     924                    case 16:
     925                        return "Spot Mode Off";
     926                    default:
     927                        return "Unknown (" + value[0] + " " + value[1] + ")";
     928                }
     929            case 1:
     930                switch (value[1]) {
     931                    case 0:
     932                        return "Spot Focusing";
     933                    case 1:
     934                        return "5-area";
     935                    default:
     936                        return "Unknown (" + value[0] + " " + value[1] + ")";
     937                }
     938            case 16:
     939                switch (value[1]) {
     940                    case 0:
     941                        return "1-area";
     942                    case 16:
     943                        return "1-area (high speed)";
     944                    default:
     945                        return "Unknown (" + value[0] + " " + value[1] + ")";
     946                }
     947            case 32:
     948                switch (value[1]) {
     949                    case 0:
     950                        return "Auto or Face Detect";
     951                    case 1:
     952                        return "3-area (left)";
     953                    case 2:
     954                        return "3-area (center)";
     955                    case 3:
     956                        return "3-area (right)";
     957                    default:
     958                        return "Unknown (" + value[0] + " " + value[1] + ")";
     959                }
     960            case 64:
     961                return "Face Detect";
     962            default:
     963                return "Unknown (" + value[0] + " " + value[1] + ")";
     964        }
     965    }
     966
     967    @Nullable
     968    public String getQualityModeDescription()
     969    {
     970        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_QUALITY_MODE);
     971        if (value == null)
     972            return null;
     973        switch (value) {
     974            case 2:
     975                return "High";
     976            case 3:
     977                return "Normal";
     978            case 6:
     979                return "Very High";
     980            case 7:
     981                return "Raw";
     982            case 9:
     983                return "Motion Picture";
     984            default:
     985                return "Unknown (" + value + ")";
     986        }
     987    }
     988
     989    @Nullable
     990    public String getVersionDescription()
     991    {
     992        return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_VERSION), 2);
     993    }
     994
     995    @Nullable
     996    public String getMakernoteVersionDescription()
     997    {
     998        return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_MAKERNOTE_VERSION), 2);
     999    }
     1000
     1001    @Nullable
     1002    public String getExifVersionDescription()
     1003    {
     1004        return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_EXIF_VERSION), 2);
     1005    }
     1006
     1007    @Nullable
     1008    public String getInternalSerialNumberDescription()
     1009    {
     1010        final byte[] bytes = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_INTERNAL_SERIAL_NUMBER);
     1011
     1012        if (bytes==null)
     1013            return null;
     1014
     1015        int length = bytes.length;
     1016        for (int index = 0; index < bytes.length; index++) {
     1017            int i = bytes[index] & 0xFF;
     1018            if (i == 0 || i > 0x7F) {
     1019                length = index;
     1020                break;
     1021            }
     1022        }
     1023
     1024        return new String(bytes, 0, length);
     1025    }
     1026
     1027    @Nullable
     1028    public String getWhiteBalanceDescription()
     1029    {
     1030        Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_WHITE_BALANCE);
     1031        if (value == null)
     1032            return null;
     1033        switch (value) {
     1034            case 1:
     1035                return "Auto";
     1036            case 2:
     1037                return "Daylight";
     1038            case 3:
     1039                return "Cloudy";
     1040            case 4:
     1041                return "Incandescent";
     1042            case 5:
     1043                return "Manual";
     1044            case 8:
     1045                return "Flash";
     1046            case 10:
     1047                return "Black & White";
     1048            case 11:
     1049                return "Manual";
     1050            case 12:
     1051                return "Shade";
     1052            default:
     1053                return "Unknown (" + value + ")";
     1054        }
     1055    }
     1056
     1057        @Nullable
     1058        public String getBabyAgeDescription()
     1059    {
     1060        final Age age = _directory.getAge(PanasonicMakernoteDirectory.TAG_BABY_AGE);
     1061        if (age==null)
     1062            return null;
     1063        return age.toFriendlyString();
     1064        }
     1065       
     1066        @Nullable
     1067        public String getBabyAge1Description()
     1068    {
     1069        final Age age = _directory.getAge(PanasonicMakernoteDirectory.TAG_BABY_AGE_1);
     1070        if (age==null)
     1071            return null;
     1072        return age.toFriendlyString();
     1073        }
     1074
     1075        @Nullable
     1076        private String getTextDescription(int tag)
     1077    {
     1078                byte[] values = _directory.getByteArray(tag);
     1079        if (values == null)
     1080            return null;
     1081        try {
     1082            return new String(values, "ASCII").trim();
     1083        } catch (UnsupportedEncodingException e) {
     1084            return null;
     1085        }
     1086        }
     1087
     1088    @Nullable
     1089    private String getOnOffDescription(int tag)
     1090    {
     1091        Integer value = _directory.getInteger(tag);
     1092        if (value == null)
     1093            return null;
     1094        switch (value) {
     1095            case 1:
     1096                return "Off";
     1097            case 2:
     1098                return "On";
     1099            default:
     1100                return "Unknown (" + value + ")";
     1101        }
     1102    }
    851103}
  • src/com/drew/metadata/exif/PanasonicMakernoteDirectory.java

     
    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;
     41
    3042    /**
    31      * 1 = On
    32      * 2 = Off
     43     * <br>
     44     * 2 = High            <br>
     45     * 3 = Normal          <br>
     46     * 6 = Very High       <br>
     47     * 7 = Raw             <br>
     48     * 9 = Motion Picture  <br>
    3349     */
    34     public static final int TAG_PANASONIC_MACRO_MODE = 0x001C;
     50    public static final int TAG_QUALITY_MODE = 0x0001;
     51    public static final int TAG_VERSION = 0x0002;
     52   
     53    /**                   
     54     * <br>
     55     * 1 = Auto            <br>
     56     * 2 = Daylight        <br>
     57     * 3 = Cloudy          <br>
     58     * 4 = Incandescent    <br>
     59     * 5 = Manual          <br>
     60     * 8 = Flash           <br>
     61     * 10 = Black & White  <br>
     62     * 11 = Manual         <br>
     63     * 12 = Shade          <br>
     64     */
     65    public static final int TAG_WHITE_BALANCE = 0x0003;
     66
     67
     68    /**                       
     69     * <br>
     70     * 1 = Auto                <br>
     71     * 2 = Manual              <br>
     72     * 4 =  Auto, Focus Button <br>
     73     * 5 = Auto, Continuous    <br>
     74     */
     75    public static final int TAG_FOCUS_MODE = 0x0007;
     76
    3577    /**
    36      * 1 = Normal
    37      * 2 = Portrait
    38      * 9 = Macro
     78     * <br>
     79     * 2 bytes                         <br>
     80     * (DMC-FZ10)                      <br>
     81     * '0 1' = Spot Mode On            <br>
     82     * '0 16' = Spot Mode Off          <br>
     83     * '(other models)                 <br>
     84     * 16 = Normal?                    <br>
     85     * '0 1' = 9-area                  <br>
     86     * '0 16' = 3-area (high speed)    <br>
     87     * '1 0' = Spot Focusing           <br>
     88     * '1 1' = 5-area                  <br>
     89     * '16 0' = 1-area                 <br>
     90     * '16 16' = 1-area (high speed)   <br>
     91     * '32 0' = Auto or Face Detect    <br>
     92     * '32 1' = 3-area (left)?         <br>
     93     * '32 2' = 3-area (center)?       <br>
     94     * '32 3' = 3-area (right)?        <br>
     95     * '64 0' = Face Detect            <br>
    3996     */
    40     public static final int TAG_PANASONIC_RECORD_MODE = 0x001F;
    41     public static final int TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
     97    public static final int TAG_AF_AREA_MODE = 0x000f;
    4298
    43     protected static final HashMap tagNameMap = new HashMap();
     99    /**
     100     * <br>
     101     * 2 = On, Mode 1   <br>
     102     * 3 = Off          <br>
     103     * 4 = On, Mode 2   <br>
     104     */
     105    public static final int TAG_IMAGE_STABILIZATION = 0x001a;
    44106
     107    /**
     108     * <br>
     109     * 1 = On    <br>
     110     * 2 = Off   <br>
     111     */
     112    public static final int TAG_MACRO_MODE = 0x001C;
     113
     114    /**
     115     * <br>
     116     * 1 = Normal                            <br>
     117     * 2 = Portrait                          <br>
     118     * 3 = Scenery                           <br>
     119     * 4 = Sports                            <br>
     120     * 5 = Night Portrait                    <br>
     121     * 6 = Program                           <br>
     122     * 7 = Aperture Priority                 <br>
     123     * 8 = Shutter Priority                  <br>
     124     * 9 = Macro                             <br>
     125     * 10= Spot                              <br>
     126     * 11= Manual                            <br>
     127     * 12= Movie Preview                     <br>
     128     * 13= Panning                           <br>
     129     * 14= Simple                            <br>
     130     * 15= Color Effects                     <br>
     131     * 16= Self Portrait                     <br>
     132     * 17= Economy                           <br>
     133     * 18= Fireworks                         <br>
     134     * 19= Party                             <br>
     135     * 20= Snow                              <br>
     136     * 21= Night Scenery                     <br>
     137     * 22= Food                              <br>
     138     * 23= Baby                              <br>
     139     * 24= Soft Skin                         <br>
     140     * 25= Candlelight                       <br>
     141     * 26= Starry Night                      <br>
     142     * 27= High Sensitivity                  <br>
     143     * 28= Panorama Assist                   <br>
     144     * 29= Underwater                        <br>
     145     * 30= Beach                             <br>
     146     * 31= Aerial Photo                      <br>
     147     * 32= Sunset                            <br>
     148     * 33= Pet                               <br>
     149     * 34= Intelligent ISO                   <br>
     150     * 35= Clipboard                         <br>
     151     * 36= High Speed Continuous Shooting    <br>
     152     * 37= Intelligent Auto                  <br>
     153     * 39= Multi-aspect                      <br>
     154     * 41= Transform                         <br>
     155     * 42= Flash Burst                       <br>
     156     * 43= Pin Hole                          <br>
     157     * 44= Film Grain                        <br>
     158     * 45= My Color                          <br>
     159     * 46= Photo Frame                       <br>
     160     * 51= HDR                               <br>
     161     */
     162    public static final int TAG_RECORD_MODE = 0x001F;
     163   
     164    /**
     165     * 1 = Yes <br>
     166     * 2 = No  <br>
     167     */
     168    public static final int TAG_AUDIO = 0x0020;
     169
     170    /**
     171     * No idea, what this is
     172     */
     173    public static final int TAG_UNKNOWN_DATA_DUMP = 0x0021;
     174   
     175    public static final int TAG_WHITE_BALANCE_BIAS = 0x0023;
     176    public static final int TAG_FLASH_BIAS = 0x0024;
     177   
     178    /**
     179     * this number is unique, and contains the date of manufacture,
     180     * but is not the same as the number printed on the camera body
     181     */
     182    public static final int TAG_INTERNAL_SERIAL_NUMBER = 0x0025;
     183
     184    /**
     185     * Panasonic Exif Version
     186     */
     187    public static final int TAG_EXIF_VERSION = 0x0026;
     188   
     189   
     190    /**
     191     * 1 = Off           <br>
     192     * 2 = Warm          <br>
     193     * 3 = Cool          <br>
     194     * 4 = Black & White <br>
     195     * 5 = Sepia         <br>
     196     */
     197    public static final int TAG_COLOR_EFFECT = 0x0028;
     198
     199    /**
     200     * 4 Bytes <br>
     201     * Time in 1/100 s from when the camera was powered on to when the
     202     * image is written to memory card
     203     */
     204    public static final int TAG_UPTIME = 0x0029;
     205
     206
     207    /**
     208     * 0 = Off        <br>
     209     * 1 = On         <br>
     210     * 2 = Infinite   <br>
     211     * 4 = Unlimited  <br>
     212     */
     213    public static final int TAG_BURST_MODE = 0x002a;
     214   
     215    public static final int TAG_SEQUENCE_NUMBER = 0x002b;
     216   
     217    /**
     218     * (this decoding seems to work for some models such as the LC1, LX2, FZ7, FZ8, FZ18 and FZ50, but may not be correct for other models such as the FX10, G1, L1, L10 and LC80) <br>
     219     * 0x0 = Normal                                            <br>
     220     * 0x1 = Low                                               <br>
     221     * 0x2 = High                                              <br>
     222     * 0x6 = Medium Low                                        <br>
     223     * 0x7 = Medium High                                       <br>
     224     * 0x100 = Low                                             <br>
     225     * 0x110 = Normal                                          <br>
     226     * 0x120 = High                                            <br>
     227     * (these values are used by the GF1)                      <br>
     228     * 0 = -2                                                  <br>
     229     * 1 = -1                                                  <br>
     230     * 2 = Normal                                              <br>
     231     * 3 = +1                                                  <br>
     232     * 4 = +2                                                  <br>
     233     * 7 = Nature (Color Film)                                 <br>
     234     * 12 = Smooth (Color Film) or Pure (My Color)             <br>
     235     * 17 = Dynamic (B&W Film)                                 <br>
     236     * 22 = Smooth (B&W Film)                                  <br>
     237     * 27 = Dynamic (Color Film)                               <br>
     238     * 32 = Vibrant (Color Film) or Expressive (My Color)      <br>
     239     * 33 = Elegant (My Color)                                 <br>
     240     * 37 = Nostalgic (Color Film)                             <br>
     241     * 41 = Dynamic Art (My Color)                             <br>
     242     * 42 = Retro (My Color)                                   <br>
     243     */
     244    public static final int TAG_CONTRAST_MODE = 0x002c;
     245   
     246   
     247    /**
     248     * 0 = Standard      <br>
     249     * 1 = Low (-1)      <br>
     250     * 2 = High (+1)     <br>
     251     * 3 = Lowest (-2)   <br>
     252     * 4 = Highest (+2)  <br>
     253     */
     254    public static final int TAG_NOISE_REDUCTION = 0x002d;
     255
     256    /**
     257     * 1 = Off   <br>
     258     * 2 = 10 s  <br>
     259     * 3 = 2 s   <br>
     260     */
     261    public static final int TAG_SELF_TIMER = 0x002e;
     262
     263    /**
     264     * 1 = 0 DG    <br>
     265     * 3 = 180 DG  <br>
     266     * 6 =  90 DG  <br>
     267     * 8 = 270 DG  <br>
     268     */
     269    public static final int TAG_ROTATION = 0x0030;
     270
     271    /**
     272     * 1 = Fired <br>
     273     * 2 = Enabled nut not used <br>
     274     * 3 = Disabled but required <br>
     275     * 4 = Disabled and not required
     276     */
     277    public static final int TAG_AF_ASSIST_LAMP = 0x0031;
     278   
     279    /**
     280     * 0 = Normal <br>
     281     * 1 = Natural<br>
     282     * 2 = Vivid
     283     *
     284     */
     285    public static final int TAG_COLOR_MODE = 0x0032;
     286   
     287    public static final int TAG_BABY_AGE = 0x0033;
     288   
     289    /**
     290     *  1 = Standard <br>
     291     *  2 = Extended
     292     */
     293    public static final int TAG_OPTICAL_ZOOM_MODE = 0x0034;
     294   
     295    /**
     296     * 1 = Off <br>
     297     * 2 = Wide <br>
     298     * 3 = Telephoto <br>
     299     * 4 = Macro
     300     */
     301    public static final int TAG_CONVERSION_LENS = 0x0035;
     302   
     303    public static final int TAG_TRAVEL_DAY = 0x0036;
     304   
     305    /**
     306     * 0 = Normal
     307     */
     308    public static final int TAG_CONTRAST = 0x0039;
     309   
     310    /**
     311     * <br>
     312     * 1 = Home <br>
     313     * 2 = Destination
     314     */
     315    public static final int TAG_WORLD_TIME_LOCATION = 0x003a;
     316   
     317    /**
     318     * 1 = Off   <br>
     319     * 2 = On
     320     */
     321    public static final int TAG_TEXT_STAMP = 0x003b;
     322
     323        public static final int TAG_PROGRAM_ISO = 0x003c;
     324   
     325    /**
     326     * <br>
     327     * 1 = Normal                               <br>
     328     * 2 = Outdoor/Illuminations/Flower/HDR Art <br>
     329     * 3 = Indoor/Architecture/Objects/HDR B&W  <br>
     330     * 4 = Creative                             <br>
     331     * 5 = Auto                                 <br>
     332     * 7 = Expressive                           <br>
     333     * 8 = Retro                                <br>
     334     * 9 = Pure                                 <br>
     335     * 10 = Elegant                             <br>
     336     * 12 = Monochrome                          <br>
     337     * 13 = Dynamic Art                         <br>
     338     * 14 = Silhouette                          <br>
     339     */
     340    public static final int TAG_ADVANCED_SCENE_MODE = 0x003d;
     341   
     342    /**
     343     * 1 = Off   <br>
     344     * 2 = On
     345     */
     346    public static final int TAG_TEXT_STAMP_1 = 0x003e;
     347       
     348    public static final int TAG_FACES_DETECTED = 0x003f;
     349
     350    public static final int TAG_SATURATION = 0x0040;
     351    public static final int TAG_SHARPNESS = 0x0041;
     352    public static final int TAG_FILM_MODE = 0x0042;
     353
     354    /**
     355         * WB adjust AB. Positive is a shift toward blue.
     356         */
     357        public static final int TAG_WB_ADJUST_AB = 0x0046;
     358    /**
     359         * WB adjust GM. Positive is a shift toward green.
     360         */
     361        public static final int TAG_WB_ADJUST_GM = 0x0047;
     362       
     363
     364    public static final int TAG_AF_POINT_POSITION = 0x004d;
     365   
     366   
     367    /**
     368     * <br>
     369     * Integer (16Bit) Indexes:                                             <br>
     370     * 0  Number Face Positions (maybe less than Faces Detected)            <br>
     371     * 1-4 Face Position 1                                                  <br>
     372     * 5-8 Face Position 2                                                  <br>
     373     * and so on                                                            <br>
     374     *                                                                      <br>
     375     * The four Integers are interpreted as follows:                        <br>
     376     * (XYWH)  X,Y Center of Face,  (W,H) Width and Height                  <br>
     377     * All values are in respect to double the size of the thumbnail image  <br>
     378     *
     379     */
     380    public static final int TAG_FACE_DETECTION_INFO = 0x004e;
     381    public static final int TAG_LENS_TYPE = 0x0051;
     382    public static final int TAG_LENS_SERIAL_NUMBER = 0x0052;
     383    public static final int TAG_ACCESSORY_TYPE = 0x0053;
     384   
     385    /**
     386     * (decoded as two 16-bit signed integers)
     387     * '-1 1' = Slim Low
     388     * '-3 2' = Slim High
     389     * '0 0' = Off
     390     * '1 1' = Stretch Low
     391     * '3 2' = Stretch High 
     392     */
     393    public static final int TAG_TRANSFORM = 0x0059;
     394   
     395    /**
     396    * 0 = Off <br>
     397    * 1 = Low <br>
     398    * 2 = Standard <br>
     399    * 3 = High
     400    */
     401    public static final int TAG_INTELLIGENT_EXPOSURE = 0x005d;
     402   
     403    /**
     404          * Info at http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
     405          *
     406     */
     407        public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
     408
     409
     410
     411    /**                                                                                   
     412     * Byte Indexes:                                                                       <br>
     413     *  0    Int (2  Byte) Number of Recognized Faces                                      <br>
     414     *  4    String(20 Byte)    Recognized Face 1 Name                                     <br>
     415     * 24    4 Int (8 Byte)     Recognized Face 1 Position  (Same Format as Face Detection)  <br>
     416     * 32    String(20 Byte)    Recognized Face 1 Age                                      <br>
     417     * 52    String(20 Byte)    Recognized Face 2 Name                                     <br>
     418     * 72    4 Int (8 Byte)     Recognized Face 2 Position  (Same Format as Face Detection)  <br>
     419     * 80    String(20 Byte)    Recognized Face 2 Age                                      <br>
     420     *                                                                                     <br>
     421     * And so on                                                                           <br>
     422     *                                                                                     <br>
     423     * The four Integers are interpreted as follows:                                       <br>
     424     * (XYWH)  X,Y Center of Face,  (W,H) Width and Height                                 <br>
     425     * All values are in respect to double the size of the thumbnail image                 <br>
     426     *
     427     */
     428    public static final int TAG_FACE_RECOGNITION_INFO = 0x0061;
     429
     430    /**
     431    * 0 = No <br>
     432    * 1 = Yes
     433    */
     434    public static final int TAG_FLASH_WARNING = 0x0062;
     435    public static final int TAG_RECOGNIZED_FACE_FLAGS = 0x0063;
     436    public static final int TAG_TITLE = 0x0065;
     437        public static final int TAG_BABY_NAME = 0x0066;
     438        public static final int TAG_LOCATION = 0x0067;
     439        public static final int TAG_COUNTRY = 0x0069;
     440    public static final int TAG_STATE = 0x006b;
     441    public static final int TAG_CITY = 0x006d;
     442    public static final int TAG_LANDMARK = 0x006f;
     443   
     444    /**
     445     * 0 = Off <br>
     446     * 2 = Auto <br>
     447     * 3 = On
     448     */
     449    public static final int TAG_INTELLIGENT_RESOLUTION = 0x0070;
     450   
     451    public static final int TAG_MAKERNOTE_VERSION = 0x8000;
     452    public static final int TAG_SCENE_MODE = 0x8001;
     453    public static final int TAG_WB_RED_LEVEL = 0x8004;
     454    public static final int TAG_WB_GREEN_LEVEL = 0x8005;
     455    public static final int TAG_WB_BLUE_LEVEL = 0x8006;
     456    public static final int TAG_FLASH_FIRED = 0x8007;
     457    public static final int TAG_TEXT_STAMP_2 = 0x8008;
     458        public static final int TAG_TEXT_STAMP_3 = 0x8009;
     459        public static final int TAG_BABY_AGE_1 = 0x8010;
     460       
     461        /**
     462     * (decoded as two 16-bit signed integers)
     463     * '-1 1' = Slim Low
     464     * '-3 2' = Slim High
     465     * '0 0' = Off
     466     * '1 1' = Stretch Low
     467     * '3 2' = Stretch High 
     468     */
     469    public static final int TAG_TRANSFORM_1 = 0x8012;
     470
     471    @NotNull
     472    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     473
    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
    54547    public PanasonicMakernoteDirectory()
     
    56549        this.setDescriptor(new PanasonicMakernoteDescriptor(this));
    57550    }
    58551
     552    @NotNull
    59553    public String getName()
    60554    {
    61555        return "Panasonic Makernote";
    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}
  • src/com/drew/metadata/exif/PentaxMakernoteDescriptor.java

     
    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 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)
    3746        {
     
    5867            case PentaxMakernoteDirectory.TAG_PENTAX_COLOUR:
    5968                return getColourDescription();
    6069            default:
    61                 return _directory.getString(tagType);
     70                return super.getDescription(tagType);
    6271        }
    6372    }
    6473
    65     public String getColourDescription() throws MetadataException
     74    @Nullable
     75    public String getColourDescription()
    6676    {
    67         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR)) return null;
    68         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR);
     77        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR);
     78        if (value==null)
     79            return null;
    6980        switch (value)
    7081        {
    7182            case 1:  return "Normal";
     
    7586        }
    7687    }
    7788
    78     public String getIsoSpeedDescription() throws MetadataException
     89    @Nullable
     90    public String getIsoSpeedDescription()
    7991    {
    80         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED)) return null;
    81         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED);
     92        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED);
     93        if (value==null)
     94            return null;
    8295        switch (value)
    8396        {
    8497            // TODO there must be other values which aren't catered for here
     
    90103        }
    91104    }
    92105
    93     public String getSaturationDescription() throws MetadataException
     106    @Nullable
     107    public String getSaturationDescription()
    94108    {
    95         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION)) return null;
    96         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION);
     109        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION);
     110        if (value==null)
     111            return null;
    97112        switch (value)
    98113        {
    99114            case 0:  return "Normal";
     
    103118        }
    104119    }
    105120
    106     public String getContrastDescription() throws MetadataException
     121    @Nullable
     122    public String getContrastDescription()
    107123    {
    108         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST)) return null;
    109         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST);
     124        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST);
     125        if (value==null)
     126            return null;
    110127        switch (value)
    111128        {
    112129            case 0:  return "Normal";
     
    116133        }
    117134    }
    118135
    119     public String getSharpnessDescription() throws MetadataException
     136    @Nullable
     137    public String getSharpnessDescription()
    120138    {
    121         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS)) return null;
    122         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS);
     139        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS);
     140        if (value==null)
     141            return null;
    123142        switch (value)
    124143        {
    125144            case 0:  return "Normal";
     
    129148        }
    130149    }
    131150
    132     public String getDigitalZoomDescription() throws MetadataException
     151    @Nullable
     152    public String getDigitalZoomDescription()
    133153    {
    134         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM)) return null;
    135         float value = _directory.getFloat(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM);
     154        Float value = _directory.getFloatObject(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM);
     155        if (value==null)
     156            return null;
    136157        if (value==0)
    137158            return "Off";
    138159        return Float.toString(value);
    139160    }
    140161
    141     public String getWhiteBalanceDescription() throws MetadataException
     162    @Nullable
     163    public String getWhiteBalanceDescription()
    142164    {
    143         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE)) return null;
    144         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE);
     165        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE);
     166        if (value==null)
     167            return null;
    145168        switch (value)
    146169        {
    147170            case 0:  return "Auto";
     
    154177        }
    155178    }
    156179
    157     public String getFlashModeDescription() throws MetadataException
     180    @Nullable
     181    public String getFlashModeDescription()
    158182    {
    159         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE)) return null;
    160         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE);
     183        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE);
     184        if (value==null)
     185            return null;
    161186        switch (value)
    162187        {
    163188            case 1:  return "Auto";
     
    168193        }
    169194    }
    170195
    171     public String getFocusModeDescription() throws MetadataException
     196    @Nullable
     197    public String getFocusModeDescription()
    172198    {
    173         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE)) return null;
    174         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE);
     199        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE);
     200        if (value==null)
     201            return null;
    175202        switch (value)
    176203        {
    177204            case 2:  return "Custom";
     
    180207        }
    181208    }
    182209
    183     public String getQualityLevelDescription() throws MetadataException
     210    @Nullable
     211    public String getQualityLevelDescription()
    184212    {
    185         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL)) return null;
    186         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL);
     213        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL);
     214        if (value==null)
     215            return null;
    187216        switch (value)
    188217        {
    189218            case 0:  return "Good";
     
    193222        }
    194223    }
    195224
    196     public String getCaptureModeDescription() throws MetadataException
     225    @Nullable
     226    public String getCaptureModeDescription()
    197227    {
    198         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE)) return null;
    199         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE);
     228        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE);
     229        if (value==null)
     230            return null;
    200231        switch (value)
    201232        {
    202233            case 1:  return "Auto";
     
    208239    }
    209240
    210241/*
    211     public String getPrintImageMatchingInfoDescription() throws MetadataException
     242    public String getPrintImageMatchingInfoDescription()
    212243    {
    213         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null;
    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
     250    public String getMacroModeDescription()
    219251    {
    220         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null;
    221         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     252        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE);
     253        if (value==null)
     254            return null;
    222255        switch (value) {
    223256            case 1:
    224257                return "On";
     
    229262        }
    230263    }
    231264
    232     public String getRecordModeDescription() throws MetadataException
     265    public String getRecordModeDescription()
    233266    {
    234         if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null;
    235         int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     267        Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE);
     268        if (value==null)
     269            return null;
    236270        switch (value) {
    237271            case 1:
    238272                return "Normal";
  • src/com/drew/metadata/exif/PentaxMakernoteDirectory.java

     
    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
    2126import java.util.HashMap;
    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
    2734{
     
    121128     */
    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
    144152    public PentaxMakernoteDirectory()
     
    146154        this.setDescriptor(new PentaxMakernoteDescriptor(this));
    147155    }
    148156
     157    @NotNull
    149158    public String getName()
    150159    {
    151160        return "Pentax Makernote";
    152161    }
    153162
    154     protected HashMap getTagNameMap()
     163    @NotNull
     164    protected HashMap<Integer, String> getTagNameMap()
    155165    {
    156         return tagNameMap;
     166        return _tagNameMap;
    157167    }
    158168}
  • src/com/drew/metadata/exif/SigmaMakernoteDescriptor.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.metadata.TagDescriptor;
     26
     27/**
     28 * Provides human-readable string representations of tag values stored in a <code>SigmaMakernoteDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
     31 */
     32public class SigmaMakernoteDescriptor extends TagDescriptor<SigmaMakernoteDirectory>
     33{
     34    public SigmaMakernoteDescriptor(@NotNull SigmaMakernoteDirectory directory)
     35    {
     36        super(directory);
     37    }
     38}
  • src/com/drew/metadata/exif/SigmaMakernoteDescriptor.java

  • src/com/drew/metadata/exif/SigmaMakernoteDirectory.java

    Modification de propriétés sur src/com/drew/metadata/exif/SigmaMakernoteDescriptor.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.metadata.Directory;
     26
     27import java.util.HashMap;
     28
     29/**
     30 * Describes tags specific to Sigma / Foveon cameras.
     31 *
     32 * @author Drew Noakes http://drewnoakes.com
     33 */
     34public class SigmaMakernoteDirectory extends Directory
     35{
     36    public static final int TAG_SERIAL_NUMBER = 2;
     37    public static final int TAG_DRIVE_MODE = 3;
     38    public static final int TAG_RESOLUTION_MODE = 4;
     39    public static final int TAG_AUTO_FOCUS_MODE = 5;
     40    public static final int TAG_FOCUS_SETTING = 6;
     41    public static final int TAG_WHITE_BALANCE = 7;
     42    public static final int TAG_EXPOSURE_MODE = 8;
     43    public static final int TAG_METERING_MODE = 9;
     44    public static final int TAG_LENS_RANGE = 10;
     45    public static final int TAG_COLOR_SPACE = 11;
     46    public static final int TAG_EXPOSURE = 12;
     47    public static final int TAG_CONTRAST = 13;
     48    public static final int TAG_SHADOW = 14;
     49    public static final int TAG_HIGHLIGHT = 15;
     50    public static final int TAG_SATURATION = 16;
     51    public static final int TAG_SHARPNESS = 17;
     52    public static final int TAG_FILL_LIGHT = 18;
     53    public static final int TAG_COLOR_ADJUSTMENT = 20;
     54    public static final int TAG_ADJUSTMENT_MODE = 21;
     55    public static final int TAG_QUALITY = 22;
     56    public static final int TAG_FIRMWARE = 23;
     57    public static final int TAG_SOFTWARE = 24;
     58    public static final int TAG_AUTO_BRACKET = 25;
     59
     60    @NotNull
     61    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     62
     63    static
     64    {
     65        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
     66        _tagNameMap.put(TAG_DRIVE_MODE, "Drive Mode");
     67        _tagNameMap.put(TAG_RESOLUTION_MODE, "Resolution Mode");
     68        _tagNameMap.put(TAG_AUTO_FOCUS_MODE, "Auto Focus Mode");
     69        _tagNameMap.put(TAG_FOCUS_SETTING, "Focus Setting");
     70        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
     71        _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode");
     72        _tagNameMap.put(TAG_METERING_MODE, "Metering Mode");
     73        _tagNameMap.put(TAG_LENS_RANGE, "Lens Range");
     74        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
     75        _tagNameMap.put(TAG_EXPOSURE, "Exposure");
     76        _tagNameMap.put(TAG_CONTRAST, "Contrast");
     77        _tagNameMap.put(TAG_SHADOW, "Shadow");
     78        _tagNameMap.put(TAG_HIGHLIGHT, "Highlight");
     79        _tagNameMap.put(TAG_SATURATION, "Saturation");
     80        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
     81        _tagNameMap.put(TAG_FILL_LIGHT, "Fill Light");
     82        _tagNameMap.put(TAG_COLOR_ADJUSTMENT, "Color Adjustment");
     83        _tagNameMap.put(TAG_ADJUSTMENT_MODE, "Adjustment Mode");
     84        _tagNameMap.put(TAG_QUALITY, "Quality");
     85        _tagNameMap.put(TAG_FIRMWARE, "Firmware");
     86        _tagNameMap.put(TAG_SOFTWARE, "Software");
     87        _tagNameMap.put(TAG_AUTO_BRACKET, "Auto Bracket");
     88    }
     89
     90
     91    public SigmaMakernoteDirectory()
     92    {
     93        this.setDescriptor(new SigmaMakernoteDescriptor(this));
     94    }
     95
     96    @NotNull
     97    public String getName()
     98    {
     99        return "Sigma Makernote";
     100    }
     101
     102    @NotNull
     103    protected HashMap<Integer, String> getTagNameMap()
     104    {
     105        return _tagNameMap;
     106    }
     107}
  • src/com/drew/metadata/exif/SigmaMakernoteDirectory.java

  • src/com/drew/metadata/exif/SonyMakernoteDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/exif/SigmaMakernoteDirectory.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  */
    15 package com.drew.metadata.exif;
    16 
    17 import com.drew.metadata.Directory;
    18 import com.drew.metadata.MetadataException;
    19 import com.drew.metadata.TagDescriptor;
    20 
    21 /**
    22  * Provides human-readable string versions of the tags stored in a SonyMakernoteDirectory.
    23  * Thanks to David Carson for the initial version of this class.
    24  */
    25 public class SonyMakernoteDescriptor extends TagDescriptor
    26 {
    27     public SonyMakernoteDescriptor(Directory directory)
    28     {
    29         super(directory);
    30     }
    31 
    32     public String getDescription(int tagType) throws MetadataException
    33     {
    34         return _directory.getString(tagType);
    35     }
    36 }
  • src/com/drew/metadata/exif/SonyMakernoteDirectory.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  */
    15 package com.drew.metadata.exif;
    16 
    17 import com.drew.metadata.Directory;
    18 
    19 import java.util.HashMap;
    20 
    21 /**
    22  * Describes tags specific to Sony cameras.
    23  */
    24 public class SonyMakernoteDirectory extends Directory
    25 {
    26         protected static final HashMap _tagNameMap = new HashMap();
    27        
    28         public String getName()
    29     {
    30                 return "Sony Makernote";
    31         }
    32 
    33         protected HashMap getTagNameMap()
    34     {
    35                 return _tagNameMap;
    36         }
    37 }
  • src/com/drew/metadata/exif/SonyType1MakernoteDescriptor.java

     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25import com.drew.metadata.TagDescriptor;
     26
     27/**
     28 * Provides human-readable string representations of tag values stored in a <code>SonyType1MakernoteDirectory</code>.
     29 * Thanks to David Carson for the initial version of this class.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     33public class SonyType1MakernoteDescriptor extends TagDescriptor<SonyType1MakernoteDirectory>
     34{
     35    public SonyType1MakernoteDescriptor(@NotNull SonyType1MakernoteDirectory directory)
     36    {
     37        super(directory);
     38    }
     39
     40    @Nullable
     41    public String getDescription(int tagType)
     42    {
     43        switch (tagType) {
     44            case SonyType1MakernoteDirectory.TAG_COLOR_TEMPERATURE:
     45                return getColorTemperatureDescription();
     46            case SonyType1MakernoteDirectory.TAG_SCENE_MODE:
     47                return getSceneModeDescription();
     48            case SonyType1MakernoteDirectory.TAG_ZONE_MATCHING:
     49                return getZoneMatchingDescription();
     50            case SonyType1MakernoteDirectory.TAG_DYNAMIC_RANGE_OPTIMISER:
     51                return getDynamicRangeOptimizerDescription();
     52            case SonyType1MakernoteDirectory.TAG_IMAGE_STABILISATION:
     53                return getImageStabilizationDescription();
     54            // Unfortunately it seems that there is no definite mapping between a lens ID and a lens model
     55            // http://gvsoft.homedns.org/exif/makernote-sony-type1.html#0xb027
     56//            case SonyType1MakernoteDirectory.TAG_LENS_ID:
     57//                return getLensIDDescription();
     58            case SonyType1MakernoteDirectory.TAG_COLOR_MODE:
     59                return getColorModeDescription();
     60            case SonyType1MakernoteDirectory.TAG_MACRO:
     61                return getMacroDescription();
     62            case SonyType1MakernoteDirectory.TAG_EXPOSURE_MODE:
     63                return getExposureModeDescription();
     64            case SonyType1MakernoteDirectory.TAG_QUALITY:
     65                return getQualityDescription();
     66            case SonyType1MakernoteDirectory.TAG_ANTI_BLUR:
     67                return getAntiBlurDescription();
     68            case SonyType1MakernoteDirectory.TAG_LONG_EXPOSURE_NOISE_REDUCTION:
     69                return getLongExposureNoiseReductionDescription();
     70            default:
     71                return super.getDescription(tagType);
     72        }
     73    }
     74
     75    @Nullable
     76    public String getColorTemperatureDescription()
     77    {
     78        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_COLOR_TEMPERATURE);
     79        if (value==null)
     80            return null;
     81        if (value==0)
     82            return "Auto";
     83        int kelvin = ((value & 0x00FF0000) >> 8) | ((value & 0xFF000000) >> 24);
     84        return String.format("%d K", kelvin);
     85    }
     86
     87    @Nullable
     88    public String getSceneModeDescription()
     89    {
     90        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_SCENE_MODE);
     91        if (value==null)
     92            return null;
     93        switch (value){
     94            case 0: return "Standard";
     95            case 1: return "Portrait";
     96            case 2: return "Text";
     97            case 3: return "Night Scene";
     98            case 4: return "Sunset";
     99            case 5: return "Sports";
     100            case 6: return "Landscape";
     101            case 7: return "Night Portrait";
     102            case 8: return "Macro";
     103            case 9: return "Super Macro";
     104            case 16: return "Auto";
     105            case 17: return "Night View/Portrait";
     106            default: return String.format("Unknown (%d)", value);
     107        }
     108    }
     109
     110    @Nullable
     111    public String getZoneMatchingDescription()
     112    {
     113        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_ZONE_MATCHING);
     114        if (value==null)
     115            return null;
     116        switch (value){
     117            case 0: return "ISO Setting Used";
     118            case 1: return "High Key";
     119            case 2: return "Low Key";
     120            default: return String.format("Unknown (%d)", value);
     121        }
     122    }
     123
     124    @Nullable
     125    public String getDynamicRangeOptimizerDescription()
     126    {
     127        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_DYNAMIC_RANGE_OPTIMISER);
     128        if (value==null)
     129            return null;
     130        switch (value){
     131            case 0: return "Off";
     132            case 1: return "Standard";
     133            case 2: return "Advanced Auto";
     134            case 8: return "Advanced LV1";
     135            case 9: return "Advanced LV2";
     136            case 10: return "Advanced LV3";
     137            case 11: return "Advanced LV4";
     138            case 12: return "Advanced LV5";
     139            default: return String.format("Unknown (%d)", value);
     140        }
     141    }
     142
     143    @Nullable
     144    public String getImageStabilizationDescription()
     145    {
     146        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_IMAGE_STABILISATION);
     147        if (value==null)
     148            return null;
     149        // Different non-zero 'on' values have been observed.  Do they mean different things?
     150        return value == 0 ? "Off" : "On";
     151    }
     152
     153    @Nullable
     154    public String getColorModeDescription()
     155    {
     156        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_COLOR_MODE);
     157        if (value==null)
     158            return null;
     159        switch (value){
     160            case 0: return "Standard";
     161            case 1: return "Vivid";
     162            case 2: return "Portrait";
     163            case 3: return "Landscape";
     164            case 4: return "Sunset";
     165            case 5: return "Night Portrait";
     166            case 6: return "Black & White";
     167            case 7: return "Adobe RGB";
     168            case 12:
     169            case 100: return "Neutral";
     170            case 101: return "Clear";
     171            case 102: return "Deep";
     172            case 103: return "Light";
     173            case 104: return "Night View";
     174            case 105: return "Autumn Leaves";
     175            default: return String.format("Unknown (%d)", value);
     176        }
     177    }
     178
     179    @Nullable
     180    public String getMacroDescription()
     181    {
     182        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_MACRO);
     183        if (value==null)
     184            return null;
     185        switch (value){
     186            case 0: return "Off";
     187            case 1: return "On";
     188            case 2: return "Magnifying Glass/Super Macro";
     189            case 0xFFFF: return "N/A";
     190            default: return String.format("Unknown (%d)", value);
     191        }
     192    }
     193
     194    @Nullable
     195    public String getExposureModeDescription()
     196    {
     197        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_EXPOSURE_MODE);
     198        if (value==null)
     199            return null;
     200        switch (value){
     201            case 0: return "Auto";
     202            case 5: return "Landscape";
     203            case 6: return "Program";
     204            case 7: return "Aperture Priority";
     205            case 8: return "Shutter Priority";
     206            case 9: return "Night Scene";
     207            case 15: return "Manual";
     208            case 0xFFFF: return "N/A";
     209            default: return String.format("Unknown (%d)", value);
     210        }
     211    }
     212
     213    @Nullable
     214    public String getQualityDescription()
     215    {
     216        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_QUALITY);
     217        if (value==null)
     218            return null;
     219        switch (value){
     220            case 0: return "Normal";
     221            case 1: return "Fine";
     222            case 0xFFFF: return "N/A";
     223            default: return String.format("Unknown (%d)", value);
     224        }
     225    }
     226
     227    @Nullable
     228    public String getAntiBlurDescription()
     229    {
     230        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_ANTI_BLUR);
     231        if (value==null)
     232            return null;
     233        switch (value){
     234            case 0: return "Off";
     235            case 1: return "On (Continuous)";
     236            case 2: return "On (Shooting)";
     237            case 0xFFFF: return "N/A";
     238            default: return String.format("Unknown (%d)", value);
     239        }
     240    }
     241
     242    @Nullable
     243    public String getLongExposureNoiseReductionDescription()
     244    {
     245        Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_LONG_EXPOSURE_NOISE_REDUCTION);
     246        if (value==null)
     247            return null;
     248        switch (value){
     249            case 0: return "Off";
     250            case 1: return "On";
     251            case 0xFFFF: return "N/A";
     252            default: return String.format("Unknown (%d)", value);
     253        }
     254    }
     255}
  • src/com/drew/metadata/exif/SonyType1MakernoteDescriptor.java

  • src/com/drew/metadata/exif/SonyType1MakernoteDirectory.java

    Modification de propriétés sur src/com/drew/metadata/exif/SonyType1MakernoteDescriptor.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.exif;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.metadata.Directory;
     25
     26import java.util.HashMap;
     27
     28/**
     29 * Describes tags specific to Sony cameras that use the Sony Type 1 makernote tags.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     33public class SonyType1MakernoteDirectory extends Directory
     34{
     35    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
     36    public static final int TAG_PREVIEW_IMAGE = 0x2001;
     37    public static final int TAG_COLOR_MODE_SETTING = 0xb020;
     38    public static final int TAG_COLOR_TEMPERATURE = 0xb021;
     39    public static final int TAG_SCENE_MODE = 0xb023;
     40    public static final int TAG_ZONE_MATCHING = 0xb024;
     41    public static final int TAG_DYNAMIC_RANGE_OPTIMISER = 0xb025;
     42    public static final int TAG_IMAGE_STABILISATION = 0xb026;
     43    public static final int TAG_LENS_ID = 0xb027;
     44    public static final int TAG_MINOLTA_MAKER_NOTE = 0xb028;
     45    public static final int TAG_COLOR_MODE = 0xb029;
     46    public static final int TAG_MACRO = 0xb040;
     47    public static final int TAG_EXPOSURE_MODE = 0xb041;
     48    public static final int TAG_QUALITY = 0xb047;
     49    public static final int TAG_ANTI_BLUR = 0xb04B;
     50    public static final int TAG_LONG_EXPOSURE_NOISE_REDUCTION = 0xb04E;
     51    public static final int TAG_NO_PRINT = 0xFFFF;
     52
     53    @NotNull
     54    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     55
     56    static
     57    {
     58        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching Info");
     59        _tagNameMap.put(TAG_PREVIEW_IMAGE, "Preview Image");
     60        _tagNameMap.put(TAG_COLOR_MODE_SETTING, "Color Mode Setting");
     61        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
     62        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
     63        _tagNameMap.put(TAG_ZONE_MATCHING, "Zone Matching");
     64        _tagNameMap.put(TAG_DYNAMIC_RANGE_OPTIMISER, "Dynamic Range Optimizer");
     65        _tagNameMap.put(TAG_IMAGE_STABILISATION, "Image Stabilisation");
     66        _tagNameMap.put(TAG_LENS_ID, "Lens ID");
     67        _tagNameMap.put(TAG_MINOLTA_MAKER_NOTE, "Minolta Maker Note");
     68        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
     69        _tagNameMap.put(TAG_MACRO, "Macro");
     70        _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode");
     71        _tagNameMap.put(TAG_QUALITY, "Quality");
     72        _tagNameMap.put(TAG_ANTI_BLUR, "Anti Blur");
     73        _tagNameMap.put(TAG_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction");
     74        _tagNameMap.put(TAG_NO_PRINT, "No Print");
     75    }
     76
     77    public SonyType1MakernoteDirectory()
     78    {
     79        this.setDescriptor(new SonyType1MakernoteDescriptor(this));
     80    }
     81
     82    @NotNull
     83    public String getName()
     84    {
     85        return "Sony Makernote";
     86    }
     87
     88    @NotNull
     89    protected HashMap<Integer, String> getTagNameMap()
     90    {
     91        return _tagNameMap;
     92    }
     93}
  • src/com/drew/metadata/exif/SonyType1MakernoteDirectory.java

  • src/com/drew/metadata/exif/SonyType6MakernoteDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/exif/SonyType1MakernoteDirectory.java
    ___________________________________________________________________
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.lang.annotations.Nullable;
     26import com.drew.metadata.TagDescriptor;
     27
     28/**
     29 * Provides human-readable string representations of tag values stored in a <code>SonyType6MakernoteDirectory</code>.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     33public class SonyType6MakernoteDescriptor extends TagDescriptor<SonyType6MakernoteDirectory>
     34{
     35    public SonyType6MakernoteDescriptor(@NotNull SonyType6MakernoteDirectory directory)
     36    {
     37        super(directory);
     38    }
     39
     40    @Nullable
     41    public String getDescription(int tagType)
     42    {
     43        switch (tagType) {
     44            case SonyType6MakernoteDirectory.TAG_MAKER_NOTE_THUMB_VERSION:
     45                return getMakerNoteThumbVersionDescription();
     46            default:
     47                return super.getDescription(tagType);
     48        }
     49    }
     50
     51    @Nullable
     52    public String getMakerNoteThumbVersionDescription()
     53    {
     54        int[] values = _directory.getIntArray(SonyType6MakernoteDirectory.TAG_MAKER_NOTE_THUMB_VERSION);
     55        return convertBytesToVersionString(values, 2);
     56    }
     57}
  • src/com/drew/metadata/exif/SonyType6MakernoteDescriptor.java

  • src/com/drew/metadata/exif/SonyType6MakernoteDirectory.java

    Modification de propriétés sur src/com/drew/metadata/exif/SonyType6MakernoteDescriptor.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21
     22package com.drew.metadata.exif;
     23
     24import com.drew.lang.annotations.NotNull;
     25import com.drew.metadata.Directory;
     26
     27import java.util.HashMap;
     28
     29/**
     30 * Describes tags specific to Sony cameras that use the Sony Type 6 makernote tags.
     31 *
     32 * @author Drew Noakes http://drewnoakes.com
     33 */
     34public class SonyType6MakernoteDirectory extends Directory
     35{
     36    public static final int TAG_MAKER_NOTE_THUMB_OFFSET = 0x0513;
     37    public static final int TAG_MAKER_NOTE_THUMB_LENGTH = 0x0514;
     38    public static final int TAG_UNKNOWN_1 = 0x0515;
     39    public static final int TAG_MAKER_NOTE_THUMB_VERSION = 0x2000;
     40
     41    @NotNull
     42    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     43
     44    static
     45    {
     46        _tagNameMap.put(TAG_MAKER_NOTE_THUMB_OFFSET, "Maker Note Thumb Offset");
     47        _tagNameMap.put(TAG_MAKER_NOTE_THUMB_LENGTH, "Maker Note Thumb Length");
     48        _tagNameMap.put(TAG_UNKNOWN_1, "Sony-6-0x0203");
     49        _tagNameMap.put(TAG_MAKER_NOTE_THUMB_VERSION, "Maker Note Thumb Version");
     50    }
     51
     52    public SonyType6MakernoteDirectory()
     53    {
     54        this.setDescriptor(new SonyType6MakernoteDescriptor(this));
     55    }
     56
     57    @NotNull
     58    public String getName()
     59    {
     60        return "Sony Makernote";
     61    }
     62
     63    @NotNull
     64    protected HashMap<Integer, String> getTagNameMap()
     65    {
     66        return _tagNameMap;
     67    }
     68}
  • src/com/drew/metadata/exif/SonyType6MakernoteDirectory.java

  • src/com/drew/metadata/Face.java

    Modification de propriétés sur src/com/drew/metadata/exif/SonyType6MakernoteDirectory.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25
     26/**
     27 * Class to hold information about a detected or recognized face in a photo.
     28 * <p/>
     29 * When a face is <em>detected</em>, the camera believes that a face is present at a given location in
     30 * the image, but is not sure whose face it is.  When a face is <em>recognised</em>, then the face is
     31 * both detected and identified as belonging to a known person.
     32 *
     33 * @author Philipp Sandhaus, Drew Noakes
     34 */
     35public class Face
     36{
     37    private final int _x;
     38    private final int _y;
     39    private final int _width;
     40    private final int _height;
     41    @Nullable
     42    private final String _name;
     43    @Nullable
     44    private final Age _age;
     45
     46    public Face(int x, int y, int width, int height, @Nullable String name, @Nullable Age age)
     47    {
     48        _x = x;
     49        _y = y;
     50        _width = width;
     51        _height = height;
     52        _name = name;
     53        _age = age;
     54    }
     55
     56    public int getX()
     57    {
     58        return _x;
     59    }
     60
     61    public int getY()
     62    {
     63        return _y;
     64    }
     65
     66    public int getWidth()
     67    {
     68        return _width;
     69    }
     70
     71    public int getHeight()
     72    {
     73        return _height;
     74    }
     75
     76    @Nullable
     77    public String getName()
     78    {
     79        return _name;
     80    }
     81
     82    @Nullable
     83    public Age getAge()
     84    {
     85        return _age;
     86    }
     87
     88    @Override
     89    public boolean equals(@Nullable Object o)
     90    {
     91        if (this == o) return true;
     92        if (o == null || getClass() != o.getClass()) return false;
     93
     94        Face face = (Face)o;
     95
     96        if (_height != face._height) return false;
     97        if (_width != face._width) return false;
     98        if (_x != face._x) return false;
     99        if (_y != face._y) return false;
     100        if (_age != null ? !_age.equals(face._age) : face._age != null) return false;
     101        if (_name != null ? !_name.equals(face._name) : face._name != null) return false;
     102
     103        return true;
     104    }
     105
     106    @Override
     107    public int hashCode()
     108    {
     109        int result = _x;
     110        result = 31 * result + _y;
     111        result = 31 * result + _width;
     112        result = 31 * result + _height;
     113        result = 31 * result + (_name != null ? _name.hashCode() : 0);
     114        result = 31 * result + (_age != null ? _age.hashCode() : 0);
     115        return result;
     116    }
     117
     118    @NotNull
     119    public String toString()
     120    {
     121        StringBuilder result = new StringBuilder();
     122        result.append("x: ").append(_x);
     123        result.append(" y: ").append(_y);
     124        result.append(" width: ").append(_width);
     125        result.append(" height: ").append(_height);
     126        if (_name != null)
     127            result.append(" name: ").append(_name);
     128        if (_age != null)
     129            result.append(" age: ").append(_age.toFriendlyString());
     130        return result.toString();
     131    }
     132}
  • src/com/drew/metadata/Face.java

  • src/com/drew/metadata/iptc/IptcDescriptor.java

    Modification de propriétés sur src/com/drew/metadata/Face.java
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Date Author Id Revision HeadURL
    \ No newline at end of property
     
    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 21-Nov-2002 17:58:19 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    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/**
     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.
    2332 *
     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        }
    3553    }
     54
     55    @Nullable
     56    public String getFileFormatDescription()
     57    {
     58        Integer value = _directory.getInteger(IptcDirectory.TAG_FILE_FORMAT);
     59        if (value == null)
     60            return null;
     61        switch (value) {
     62            case 0: return "No ObjectData";
     63            case 1: return "IPTC-NAA Digital Newsphoto Parameter Record";
     64            case 2: return "IPTC7901 Recommended Message Format";
     65            case 3: return "Tagged Image File Format (Adobe/Aldus Image data)";
     66            case 4: return "Illustrator (Adobe Graphics data)";
     67            case 5: return "AppleSingle (Apple Computer Inc)";
     68            case 6: return "NAA 89-3 (ANPA 1312)";
     69            case 7: return "MacBinary II";
     70            case 8: return "IPTC Unstructured Character Oriented File Format (UCOFF)";
     71            case 9: return "United Press International ANPA 1312 variant";
     72            case 10: return "United Press International Down-Load Message";
     73            case 11: return "JPEG File Interchange (JFIF)";
     74            case 12: return "Photo-CD Image-Pac (Eastman Kodak)";
     75            case 13: return "Bit Mapped Graphics File [.BMP] (Microsoft)";
     76            case 14: return "Digital Audio File [.WAV] (Microsoft & Creative Labs)";
     77            case 15: return "Audio plus Moving Video [.AVI] (Microsoft)";
     78            case 16: return "PC DOS/Windows Executable Files [.COM][.EXE]";
     79            case 17: return "Compressed Binary File [.ZIP] (PKWare Inc)";
     80            case 18: return "Audio Interchange File Format AIFF (Apple Computer Inc)";
     81            case 19: return "RIFF Wave (Microsoft Corporation)";
     82            case 20: return "Freehand (Macromedia/Aldus)";
     83            case 21: return "Hypertext Markup Language [.HTML] (The Internet Society)";
     84            case 22: return "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC";
     85            case 23: return "MPEG 2 Audio Layer 3, ISO/IEC";
     86            case 24: return "Portable Document File [.PDF] Adobe";
     87            case 25: return "News Industry Text Format (NITF)";
     88            case 26: return "Tape Archive [.TAR]";
     89            case 27: return "Tidningarnas Telegrambyra NITF version (TTNITF DTD)";
     90            case 28: return "Ritzaus Bureau NITF version (RBNITF DTD)";
     91            case 29: return "Corel Draw [.CDR]";
     92        }
     93        return String.format("Unknown (%d)", value);
     94    }
     95
     96    @Nullable
     97    public String getByLineDescription()
     98    {
     99        return _directory.getString(IptcDirectory.TAG_BY_LINE);
     100    }
     101
     102    @Nullable
     103    public String getByLineTitleDescription()
     104    {
     105        return _directory.getString(IptcDirectory.TAG_BY_LINE_TITLE);
     106    }
     107
     108    @Nullable
     109    public String getCaptionDescription()
     110    {
     111        return _directory.getString(IptcDirectory.TAG_CAPTION);
     112    }
     113
     114    @Nullable
     115    public String getCategoryDescription()
     116    {
     117        return _directory.getString(IptcDirectory.TAG_CATEGORY);
     118    }
     119
     120    @Nullable
     121    public String getCityDescription()
     122    {
     123        return _directory.getString(IptcDirectory.TAG_CITY);
     124    }
     125
     126    @Nullable
     127    public String getCopyrightNoticeDescription()
     128    {
     129        return _directory.getString(IptcDirectory.TAG_COPYRIGHT_NOTICE);
     130    }
     131
     132    @Nullable
     133    public String getCountryOrPrimaryLocationDescription()
     134    {
     135        return _directory.getString(IptcDirectory.TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME);
     136    }
     137
     138    @Nullable
     139    public String getCreditDescription()
     140    {
     141        return _directory.getString(IptcDirectory.TAG_CREDIT);
     142    }
     143
     144    @Nullable
     145    public String getDateCreatedDescription()
     146    {
     147        return _directory.getString(IptcDirectory.TAG_DATE_CREATED);
     148    }
     149
     150    @Nullable
     151    public String getHeadlineDescription()
     152    {
     153        return _directory.getString(IptcDirectory.TAG_HEADLINE);
     154    }
     155
     156    @Nullable
     157    public String getKeywordsDescription()
     158    {
     159        final String[] keywords = _directory.getStringArray(IptcDirectory.TAG_KEYWORDS);
     160        if (keywords==null)
     161            return null;
     162        return StringUtil.join(keywords, ";");
     163    }
     164
     165    @Nullable
     166    public String getObjectNameDescription()
     167    {
     168        return _directory.getString(IptcDirectory.TAG_OBJECT_NAME);
     169    }
     170
     171    @Nullable
     172    public String getOriginalTransmissionReferenceDescription()
     173    {
     174        return _directory.getString(IptcDirectory.TAG_ORIGINAL_TRANSMISSION_REFERENCE);
     175    }
     176
     177    @Nullable
     178    public String getOriginatingProgramDescription()
     179    {
     180        return _directory.getString(IptcDirectory.TAG_ORIGINATING_PROGRAM);
     181    }
     182
     183    @Nullable
     184    public String getProvinceOrStateDescription()
     185    {
     186        return _directory.getString(IptcDirectory.TAG_PROVINCE_OR_STATE);
     187    }
     188
     189    @Nullable
     190    public String getRecordVersionDescription()
     191    {
     192        return _directory.getString(IptcDirectory.TAG_APPLICATION_RECORD_VERSION);
     193    }
     194
     195    @Nullable
     196    public String getReleaseDateDescription()
     197    {
     198        return _directory.getString(IptcDirectory.TAG_RELEASE_DATE);
     199    }
     200
     201    @Nullable
     202    public String getReleaseTimeDescription()
     203    {
     204        return _directory.getString(IptcDirectory.TAG_RELEASE_TIME);
     205    }
     206
     207    @Nullable
     208    public String getSourceDescription()
     209    {
     210        return _directory.getString(IptcDirectory.TAG_SOURCE);
     211    }
     212
     213    @Nullable
     214    public String getSpecialInstructionsDescription()
     215    {
     216        return _directory.getString(IptcDirectory.TAG_SPECIAL_INSTRUCTIONS);
     217    }
     218
     219    @Nullable
     220    public String getSupplementalCategoriesDescription()
     221    {
     222        return _directory.getString(IptcDirectory.TAG_SUPPLEMENTAL_CATEGORIES);
     223    }
     224
     225    @Nullable
     226    public String getTimeCreatedDescription()
     227    {
     228        return _directory.getString(IptcDirectory.TAG_TIME_CREATED);
     229    }
     230
     231    @Nullable
     232    public String getUrgencyDescription()
     233    {
     234        return _directory.getString(IptcDirectory.TAG_URGENCY);
     235    }
     236
     237    @Nullable
     238    public String getWriterDescription()
     239    {
     240        return _directory.getString(IptcDirectory.TAG_CAPTION_WRITER);
     241    }
    36242}
  • src/com/drew/metadata/iptc/IptcDirectory.java

     
    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 01:26:39 using IntelliJ IDEA.
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
    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/**
     32 * Describes tags used by the International Press Telecommunications Council (IPTC) metadata format.
    2433 *
     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;
     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
    5253
    53     protected static final HashMap tagNameMap = new HashMap();
     54    // IPTC ApplicationRecord Tags
     55    public static final int TAG_APPLICATION_RECORD_VERSION       = 0x0200; // 0 + 0x0200
     56    public static final int TAG_OBJECT_TYPE_REFERENCE            = 0x0203; // 3
     57    public static final int TAG_OBJECT_ATTRIBUTE_REFERENCE       = 0x0204; // 4
     58    public static final int TAG_OBJECT_NAME                      = 0x0205; // 5
     59    public static final int TAG_EDIT_STATUS                      = 0x0207; // 7
     60    public static final int TAG_EDITORIAL_UPDATE                 = 0x0208; // 8
     61    public static final int TAG_URGENCY                          = 0X020A; // 10
     62    public static final int TAG_SUBJECT_REFERENCE                = 0X020C; // 12
     63    public static final int TAG_CATEGORY                         = 0x020F; // 15
     64    public static final int TAG_SUPPLEMENTAL_CATEGORIES          = 0x0214; // 20
     65    public static final int TAG_FIXTURE_ID                       = 0x0216; // 22
     66    public static final int TAG_KEYWORDS                         = 0x0219; // 25
     67    public static final int TAG_CONTENT_LOCATION_CODE            = 0x021A; // 26
     68    public static final int TAG_CONTENT_LOCATION_NAME            = 0x021B; // 27
     69    public static final int TAG_RELEASE_DATE                     = 0X021E; // 30
     70    public static final int TAG_RELEASE_TIME                     = 0x0223; // 35
     71    public static final int TAG_EXPIRATION_DATE                  = 0x0225; // 37
     72    public static final int TAG_EXPIRATION_TIME                  = 0x0226; // 38
     73    public static final int TAG_SPECIAL_INSTRUCTIONS             = 0x0228; // 40
     74    public static final int TAG_ACTION_ADVISED                   = 0x022A; // 42
     75    public static final int TAG_REFERENCE_SERVICE                = 0x022D; // 45
     76    public static final int TAG_REFERENCE_DATE                   = 0x022F; // 47
     77    public static final int TAG_REFERENCE_NUMBER                 = 0x0232; // 50
     78    public static final int TAG_DATE_CREATED                     = 0x0237; // 55
     79    public static final int TAG_TIME_CREATED                     = 0X023C; // 60
     80    public static final int TAG_DIGITAL_DATE_CREATED             = 0x023E; // 62
     81    public static final int TAG_DIGITAL_TIME_CREATED             = 0x023F; // 63
     82    public static final int TAG_ORIGINATING_PROGRAM              = 0x0241; // 65
     83    public static final int TAG_PROGRAM_VERSION                  = 0x0246; // 70
     84    public static final int TAG_OBJECT_CYCLE                     = 0x024B; // 75
     85    public static final int TAG_BY_LINE                          = 0x0250; // 80
     86    public static final int TAG_BY_LINE_TITLE                    = 0x0255; // 85
     87    public static final int TAG_CITY                             = 0x025A; // 90
     88    public static final int TAG_SUB_LOCATION                     = 0x025C; // 92
     89    public static final int TAG_PROVINCE_OR_STATE                = 0x025F; // 95
     90    public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE = 0x0264; // 100
     91    public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME = 0x0265; // 101
     92    public static final int TAG_ORIGINAL_TRANSMISSION_REFERENCE  = 0x0267; // 103
     93    public static final int TAG_HEADLINE                         = 0x0269; // 105
     94    public static final int TAG_CREDIT                           = 0x026E; // 110
     95    public static final int TAG_SOURCE                           = 0x0273; // 115
     96    public static final int TAG_COPYRIGHT_NOTICE                 = 0x0274; // 116
     97    public static final int TAG_CONTACT                          = 0x0276; // 118
     98    public static final int TAG_CAPTION                          = 0x0278; // 120
     99    public static final int TAG_LOCAL_CAPTION                    = 0x0279; // 121
     100    public static final int TAG_CAPTION_WRITER                   = 0x027A; // 122
     101    public static final int TAG_RASTERIZED_CAPTION               = 0x027D; // 125
     102    public static final int TAG_IMAGE_TYPE                       = 0x0282; // 130
     103    public static final int TAG_IMAGE_ORIENTATION                = 0x0283; // 131
     104    public static final int TAG_LANGUAGE_IDENTIFIER              = 0x0287; // 135
     105    public static final int TAG_AUDIO_TYPE                       = 0x0296; // 150
     106    public static final int TAG_AUDIO_SAMPLING_RATE              = 0x0297; // 151
     107    public static final int TAG_AUDIO_SAMPLING_RESOLUTION        = 0x0298; // 152
     108    public static final int TAG_AUDIO_DURATION                   = 0x0299; // 153
     109    public static final int TAG_AUDIO_OUTCUE                     = 0x029A; // 154
    54110
     111    public static final int TAG_JOB_ID                           = 0x02B8; // 184
     112    public static final int TAG_MASTER_DOCUMENT_ID               = 0x02B9; // 185
     113    public static final int TAG_SHORT_DOCUMENT_ID                = 0x02BA; // 186
     114    public static final int TAG_UNIQUE_DOCUMENT_ID               = 0x02BB; // 187
     115    public static final int TAG_OWNER_ID                         = 0x02BC; // 188
     116
     117    public static final int TAG_OBJECT_PREVIEW_FILE_FORMAT       = 0x02C8; // 200
     118    public static final int TAG_OBJECT_PREVIEW_FILE_FORMAT_VERSION  = 0x02C9; // 201
     119    public static final int TAG_OBJECT_PREVIEW_DATA              = 0x02CA; // 202
     120
     121    @NotNull
     122    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     123
    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
    83208    public IptcDirectory()
     
    85210        this.setDescriptor(new IptcDescriptor(this));
    86211    }
    87212
     213    @NotNull
    88214    public String getName()
    89215    {
    90216        return "Iptc";
    91217    }
    92218
    93     protected HashMap getTagNameMap()
     219    @NotNull
     220    protected HashMap<Integer, String> getTagNameMap()
    94221    {
    95         return tagNameMap;
     222        return _tagNameMap;
    96223    }
     224
     225    /**
     226     * Returns any keywords contained in the IPTC data.  This value may be <code>null</code>.
     227     */
     228    @Nullable
     229    public List<String> getKeywords()
     230    {
     231        final String[] array = getStringArray(IptcDirectory.TAG_KEYWORDS);
     232        if (array==null)
     233            return null;
     234        return Arrays.asList(array);
     235    }
    97236}
  • src/com/drew/metadata/iptc/IptcProcessingException.java

     
    1 /*
    2  * ExifProcessingException.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.  I do ask that you leave this header in tact.
    8  *
    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.
    11  *
    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/
    16  *
    17  * Created on 29 April 2002, 00:33
    18  */
    19 
    20 package com.drew.metadata.iptc;
    21 
    22 import com.drew.metadata.MetadataException;
    23 
    24 /**
    25  * The exception type raised during reading of Iptc data in the instance of
    26  * unexpected data conditions.
    27  * @author  Drew Noakes http://drewnoakes.com
    28  */
    29 public class IptcProcessingException extends MetadataException
    30 {
    31     /**
    32      * Constructs an instance of <code>ExifProcessingException</code> with the
    33      * specified detail message.
    34      * @param message the detail message
    35      */
    36     public IptcProcessingException(String message)
    37     {
    38         super(message);
    39     }
    40 
    41     /**
    42      * Constructs an instance of <code>IptcProcessingException</code> with the
    43      * specified detail message and inner exception.
    44      * @param message the detail message
    45      * @param cause an inner exception
    46      */
    47     public IptcProcessingException(String message, Throwable cause)
    48     {
    49         super(message, cause);
    50     }
    51 }
  • src/com/drew/metadata/iptc/IptcReader.java

     
    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;
    3742
     
    4550    public static final int DATA_RECORD = 8;
    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;
    91         }
    92 
    93         Directory directory = metadata.getDirectory(IptcDirectory.class);
    94 
    95         // find start of data
    96         int offset = 0;
     61/*
     62        // find start-of-segment marker (potentially need to skip some ASCII photoshop header info)
    9763        try {
    98             while (offset < _data.length - 1 && get32Bits(offset) != 0x1c02) {
     64            while (offset < data.length - 1 && reader.getUInt16(offset) != 0x1c01 && reader.getUInt16(offset) != 0x1c02)
    9965                offset++;
    100             }
    101         } catch (MetadataException e) {
    102             directory.addError("Couldn't find start of Iptc data (invalid segment)");
    103             return metadata;
     66        } catch (BufferBoundsException e) {
     67            directory.addError("Couldn't find start of IPTC data (invalid segment)");
     68            return;
    10469        }
     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            }
    11694
     
    12098            int tagType;
    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
     126    private void processTag(@NotNull BufferReader reader, @NotNull Directory directory, int directoryType, int tagType, int offset, int tagByteCount) throws BufferBoundsException
    149127    {
    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)
    161     {
    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:
    175141            case IptcDirectory.TAG_DATE_CREATED:
    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;
    186152                    } catch (NumberFormatException e) {
     
    193159            default:
    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;
    220182            directory.setStringArray(tagIdentifier, newStrings);
  • src/com/drew/metadata/jpeg/JpegCommentDescriptor.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on Oct 10, 2003 using IntelliJ IDEA.
    16  */
    17 package com.drew.metadata.jpeg;
    18 
    19 import com.drew.metadata.Directory;
    20 import com.drew.metadata.TagDescriptor;
    21 
    22 /**
    23  *
    24  * @author Drew Noakes http://drewnoakes.com
    25  */
    26 public class JpegCommentDescriptor extends TagDescriptor
    27 {
    28     public JpegCommentDescriptor(Directory directory)
    29     {
    30         super(directory);
    31     }
    32 
    33     public String getDescription(int tagType)
    34     {
    35         return _directory.getString(tagType);
    36     }
    37 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.jpeg;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.lang.annotations.Nullable;
     25import com.drew.metadata.TagDescriptor;
     26
     27/**
     28 * Provides human-readable string representations of tag values stored in a <code>JpegCommentDirectory</code>.
     29 *
     30 * @author Drew Noakes http://drewnoakes.com
     31 */
     32public class JpegCommentDescriptor extends TagDescriptor<JpegCommentDirectory>
     33{
     34    public JpegCommentDescriptor(@NotNull JpegCommentDirectory directory)
     35    {
     36        super(directory);
     37    }
     38
     39    @Nullable
     40    public String getJpegCommentDescription()
     41    {
     42        return _directory.getString(JpegCommentDirectory.TAG_JPEG_COMMENT);
     43    }
     44}
  • src/com/drew/metadata/jpeg/JpegCommentDirectory.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on Oct 10, 2003 using IntelliJ IDEA.
    16  */
    17 package com.drew.metadata.jpeg;
    18 
    19 import com.drew.metadata.Directory;
    20 
    21 import java.util.HashMap;
    22 
    23 /**
    24  *
    25  * @author Drew Noakes http://drewnoakes.com
    26  */
    27 public class JpegCommentDirectory extends Directory {
    28 
    29         /** This is in bits/sample, usually 8 (12 and 16 not supported by most software). */
    30         public static final int TAG_JPEG_COMMENT = 0;
    31 
    32         protected static final HashMap tagNameMap = new HashMap();
    33 
    34         static {
    35         tagNameMap.put(new Integer(TAG_JPEG_COMMENT), "Jpeg Comment");
    36         }
    37 
    38     public JpegCommentDirectory() {
    39                 this.setDescriptor(new JpegCommentDescriptor(this));
    40         }
    41 
    42         public String getName() {
    43                 return "JpegComment";
    44         }
    45 
    46         protected HashMap getTagNameMap() {
    47                 return tagNameMap;
    48         }
    49 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.jpeg;
     22
     23import com.drew.lang.annotations.NotNull;
     24import com.drew.metadata.Directory;
     25
     26import java.util.HashMap;
     27
     28/**
     29 * Describes tags used by a JPEG file comment.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     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;
     40
     41    @NotNull
     42    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     43
     44    static {
     45        _tagNameMap.put(TAG_JPEG_COMMENT, "Jpeg Comment");
     46    }
     47
     48    public JpegCommentDirectory()
     49    {
     50        this.setDescriptor(new JpegCommentDescriptor(this));
     51    }
     52
     53    @NotNull
     54    public String getName()
     55    {
     56        return "JpegComment";
     57    }
     58
     59    @NotNull
     60    protected HashMap<Integer, String> getTagNameMap()
     61    {
     62        return _tagNameMap;
     63    }
     64}
  • src/com/drew/metadata/jpeg/JpegCommentReader.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on Oct 10, 2003 using IntelliJ IDEA.
    16  */
    17 package com.drew.metadata.jpeg;
    18 
    19 import com.drew.imaging.jpeg.JpegProcessingException;
    20 import com.drew.imaging.jpeg.JpegSegmentReader;
    21 import com.drew.metadata.Metadata;
    22 import com.drew.metadata.MetadataReader;
    23 
    24 import java.io.File;
    25 import java.io.InputStream;
    26 
    27 /**
    28  *
    29  * @author Drew Noakes http://drewnoakes.com
    30  */
    31 public class JpegCommentReader implements MetadataReader
    32 {
    33     /**
    34      * The COM data segment.
    35      */
    36     private final byte[] _data;
    37 
    38     /**
    39      * Creates a new JpegReader for the specified Jpeg jpegFile.
    40      */
    41     public JpegCommentReader(File jpegFile) throws JpegProcessingException
    42     {
    43         this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_COM));
    44     }
    45 
    46     /** Creates a JpegCommentReader for a JPEG stream.
    47      *
    48      * @param is JPEG stream. Stream will be closed.
    49      */
    50     public JpegCommentReader(InputStream is) throws JpegProcessingException
    51     {
    52         this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APPD));
    53     }
    54 
    55     public JpegCommentReader(byte[] data)
    56     {
    57         _data = data;
    58     }
    59 
    60     /**
    61      * Performs the Jpeg data extraction, returning a new instance of <code>Metadata</code>.
    62      */
    63     public Metadata extract()
    64     {
    65         return extract(new Metadata());
    66     }
    67 
    68     /**
    69      * Performs the Jpeg data extraction, adding found values to the specified
    70      * instance of <code>Metadata</code>.
    71      */
    72     public Metadata extract(Metadata metadata)
    73     {
    74         if (_data==null) {
    75             return metadata;
    76         }
    77 
    78         JpegCommentDirectory directory = (JpegCommentDirectory)metadata.getDirectory(JpegCommentDirectory.class);
    79 
    80         directory.setString(JpegCommentDirectory.TAG_JPEG_COMMENT, new String(_data));
    81 
    82         return metadata;
    83     }
    84 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.jpeg;
     22
     23import com.drew.lang.BufferBoundsException;
     24import com.drew.lang.BufferReader;
     25import com.drew.lang.annotations.NotNull;
     26import com.drew.metadata.Metadata;
     27import com.drew.metadata.MetadataReader;
     28
     29/**
     30 * Decodes the comment stored within Jpeg files, populating a <code>Metadata</code> object with tag values in a
     31 * <code>JpegCommentDirectory</code>.
     32 *
     33 * @author Drew Noakes http://drewnoakes.com
     34 */
     35public class JpegCommentReader implements MetadataReader
     36{
     37    /**
     38     * Performs the Jpeg data extraction, adding found values to the specified
     39     * instance of <code>Metadata</code>.
     40     */
     41    public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata)
     42    {
     43        JpegCommentDirectory directory = metadata.getOrCreateDirectory(JpegCommentDirectory.class);
     44
     45        try {
     46            directory.setString(JpegCommentDirectory.TAG_JPEG_COMMENT, reader.getString(0, (int)reader.getLength()));
     47        } catch (BufferBoundsException e) {
     48            directory.addError("Exception reading JPEG comment string");
     49        }
     50    }
     51}
  • src/com/drew/metadata/jpeg/JpegComponent.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on Oct 9, 17:04:07 using IntelliJ IDEA.
    16  */
    17 package com.drew.metadata.jpeg;
    18 
    19 import com.drew.metadata.MetadataException;
    20 
    21 import java.io.Serializable;
    22 
    23 /**
    24  * Created by IntelliJ IDEA.
    25  * User: dnoakes
    26  * Date: 09-Oct-2003
    27  * Time: 17:04:07
    28  * To change this template use Options | File Templates.
    29  */
    30 public class JpegComponent implements Serializable
    31 {
    32     private final int _componentId;
    33     private final int _samplingFactorByte;
    34     private final int _quantizationTableNumber;
    35 
    36     public JpegComponent(int componentId, int samplingFactorByte, int quantizationTableNumber)
    37     {
    38         _componentId = componentId;
    39         _samplingFactorByte = samplingFactorByte;
    40         _quantizationTableNumber = quantizationTableNumber;
    41     }
    42 
    43     public int getComponentId()
    44     {
    45         return _componentId;
    46     }
    47 
    48     public String getComponentName() throws MetadataException
    49     {
    50         switch (_componentId)
    51         {
    52             case 1:
    53                 return "Y";
    54             case 2:
    55                 return "Cb";
    56             case 3:
    57                 return "Cr";
    58             case 4:
    59                 return "I";
    60             case 5:
    61                 return "Q";
    62         }
    63 
    64         throw new MetadataException("Unsupported component id: " + _componentId);
    65     }
    66 
    67     public int getQuantizationTableNumber()
    68     {
    69         return _quantizationTableNumber;
    70     }
    71 
    72     public int getHorizontalSamplingFactor()
    73     {
    74         return _samplingFactorByte & 0x0F;
    75     }
    76 
    77     public int getVerticalSamplingFactor()
    78     {
    79         return (_samplingFactorByte>>4) & 0x0F;
    80     }
    81 }
     1/*
     2 * Copyright 2002-2012 Drew Noakes
     3 *
     4 *    Licensed under the Apache License, Version 2.0 (the "License");
     5 *    you may not use this file except in compliance with the License.
     6 *    You may obtain a copy of the License at
     7 *
     8 *        http://www.apache.org/licenses/LICENSE-2.0
     9 *
     10 *    Unless required by applicable law or agreed to in writing, software
     11 *    distributed under the License is distributed on an "AS IS" BASIS,
     12 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 *    See the License for the specific language governing permissions and
     14 *    limitations under the License.
     15 *
     16 * More information about this project is available at:
     17 *
     18 *    http://drewnoakes.com/code/exif/
     19 *    http://code.google.com/p/metadata-extractor/
     20 */
     21package com.drew.metadata.jpeg;
     22
     23import com.drew.lang.annotations.Nullable;
     24
     25import java.io.Serializable;
     26
     27/**
     28 * Stores information about a Jpeg image component such as the component id, horiz/vert sampling factor and
     29 * quantization table number.
     30 *
     31 * @author Drew Noakes http://drewnoakes.com
     32 */
     33public class JpegComponent implements Serializable
     34{
     35    private static final long serialVersionUID = 61121257899091914L;
     36
     37    private final int _componentId;
     38    private final int _samplingFactorByte;
     39    private final int _quantizationTableNumber;
     40
     41    public JpegComponent(int componentId, int samplingFactorByte, int quantizationTableNumber)
     42    {
     43        _componentId = componentId;
     44        _samplingFactorByte = samplingFactorByte;
     45        _quantizationTableNumber = quantizationTableNumber;
     46    }
     47
     48    public int getComponentId()
     49    {
     50        return _componentId;
     51    }
     52
     53    /**
     54     * Returns the component name (one of: Y, Cb, Cr, I, or Q)
     55     * @return the component name
     56     */
     57    @Nullable
     58    public String getComponentName()
     59    {
     60        switch (_componentId)
     61        {
     62            case 1:
     63                return "Y";
     64            case 2:
     65                return "Cb";
     66            case 3:
     67                return "Cr";
     68            case 4:
     69                return "I";
     70            case 5:
     71                return "Q";
     72        }
     73        return null;
     74    }
     75
     76    public int getQuantizationTableNumber()
     77    {
     78        return _quantizationTableNumber;
     79    }
     80
     81    public int getHorizontalSamplingFactor()
     82    {
     83        return _samplingFactorByte & 0x0F;
     84    }
     85
     86    public int getVerticalSamplingFactor()
     87    {
     88        return (_samplingFactorByte>>4) & 0x0F;
     89    }
     90}
  • src/com/drew/metadata/jpeg/JpegDescriptor.java

     
    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
    2127/**
    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);
    3849            case JpegDirectory.TAG_JPEG_COMPONENT_DATA_2:
     
    4758                return getImageHeightDescription();
    4859            case JpegDirectory.TAG_JPEG_IMAGE_WIDTH:
    4960                return getImageWidthDescription();
     61            default:
     62                return super.getDescription(tagType);
    5063        }
     64    }
    5165
    52         return _directory.getString(tagType);
     66    @Nullable
     67    public String getImageCompressionTypeDescription()
     68    {
     69        Integer value = _directory.getInteger(JpegDirectory.TAG_JPEG_COMPRESSION_TYPE);
     70        if (value==null)
     71            return null;
     72        // Note there is no 2 or 12
     73        switch (value) {
     74            case 0: return "Baseline";
     75            case 1: return "Extended sequential, Huffman";
     76            case 2: return "Progressive, Huffman";
     77            case 3: return "Lossless, Huffman";
     78            case 5: return "Differential sequential, Huffman";
     79            case 6: return "Differential progressive, Huffman";
     80            case 7: return "Differential lossless, Huffman";
     81            case 8: return "Reserved for JPEG extensions";
     82            case 9: return "Extended sequential, arithmetic";
     83            case 10: return "Progressive, arithmetic";
     84            case 11: return "Lossless, arithmetic";
     85            case 13: return "Differential sequential, arithmetic";
     86            case 14: return "Differential progressive, arithmetic";
     87            case 15: return "Differential lossless, arithmetic";
     88            default:
     89                return "Unknown type: "+ value;
     90        }
    5391    }
    54 
     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();
    87137    }
  • src/com/drew/metadata/jpeg/JpegDirectory.java

     
    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;
    2127
     
    2329
    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 {
     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;
    2954
    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;
    45 
    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
    97110    public int getImageWidth() throws MetadataException
  • src/com/drew/metadata/jpeg/JpegReader.java

     
    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         }
     42        JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class);
    7843
    79         JpegDirectory directory = (JpegDirectory)metadata.getDirectory(JpegDirectory.class);
    80 
    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
    9861            // for each component, there are three bytes of data:
     
    10063            // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
    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;
    11777    }
    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);
    145     }
    14678}
  • src/com/drew/metadata/Metadata.java

     
    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{
     42    @NotNull
     43    private final Map<Class<? extends Directory>,Directory> _directoryByClass = new HashMap<Class<? extends Directory>, Directory>();
     44   
    4345    /**
    44      *
    45      */
    46     private final HashMap directoryMap;
    47 
    48     /**
    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();
     60        return _directoryList;
    6161    }
    6262
    63 
    64 // OTHER METHODS
    65 
    6663    /**
    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();
    75     }
    76 
    77     /**
    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
    8673    /**
    8774     * Returns a <code>Directory</code> of specified type.  If this <code>Metadata</code> object already contains
    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();
    10595        } catch (Exception e) {
    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;
    112103    }
    113104
    114105    /**
     106     * If this <code>Metadata</code> object contains a <code>Directory</code> of the specified type, it is returned.
     107     * Otherwise <code>null</code> is returned.
     108     *
     109     * @param type the Directory type
     110     * @param <T> the Directory type
     111     * @return a Directory of type T if it exists in this Metadata object, otherwise <code>null</code>.
     112     */
     113    @Nullable
     114    @SuppressWarnings("unchecked")
     115    public <T extends Directory> T getDirectory(@NotNull Class<T> type)
     116    {
     117        // We suppress the warning here as the code asserts a map signature of Class<T>,T.
     118        // So after get(Class<T>) it is for sure the result is from type T.
     119
     120        return (T)_directoryByClass.get(type);
     121    }
     122
     123    /**
    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);
    123133    }
     134
     135    /**
     136     * Indicates whether any errors were reported during the reading of metadata values.
     137     * This value will be true if Directory.hasErrors() is true for one of the contained Directory objects.
     138     *
     139     * @return whether one of the contained directories has an error
     140     */
     141    public boolean hasErrors()
     142    {
     143        for (Directory directory : _directoryList) {
     144            if (directory.hasErrors())
     145                return true;
     146        }
     147        return false;
     148    }
    124149}
  • src/com/drew/metadata/MetadataException.java

     
    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);
    3948    }
  • src/com/drew/metadata/MetadataReader.java

     
    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}
  • src/com/drew/metadata/SampleUsage.java

     
    1 /*
    2  * This is public domain software - that is, you can do whatever you want
    3  * with it, and include it software that is licensed under the GNU or the
    4  * BSD license, or whatever other licence you choose, including proprietary
    5  * closed source licenses.  I do ask that you leave this header in tact.
    6  *
    7  * If you make modifications to this code that you think would benefit the
    8  * wider community, please send me a copy and I'll post it on my site.
    9  *
    10  * If you make use of this code, I'd appreciate hearing about it.
    11  *   drew@drewnoakes.com
    12  * Latest version of this software kept at
    13  *   http://drewnoakes.com/
    14  *
    15  * Created by dnoakes on 05-Nov-2002 18:57:14 using IntelliJ IDEA.
    16  */
    17 package com.drew.metadata;
    18 
    19 import com.drew.imaging.jpeg.JpegMetadataReader;
    20 import com.drew.imaging.jpeg.JpegProcessingException;
    21 import com.drew.imaging.jpeg.JpegSegmentReader;
    22 import com.drew.metadata.exif.ExifReader;
    23 import com.drew.metadata.iptc.IptcReader;
    24 
    25 import java.awt.image.BufferedImage;
    26 import java.io.File;
    27 import java.io.FileInputStream;
    28 import java.io.FileNotFoundException;
    29 import java.io.IOException;
    30 import java.util.Iterator;
    31 
    32 /**
    33  *
    34  */
    35 public class SampleUsage
    36 {
    37     /**
    38      * Constructor which executes multiple sample usages, each of which return the same output.  This class showcases
    39      * multiple usages of this metadata class library.
    40      * @param fileName path to a jpeg file upon which to operate
    41      */
    42     public SampleUsage(String fileName)
    43     {
    44         File jpegFile = new File(fileName);
    45 
    46         // There are multiple ways to get a Metadata object
    47 
    48         // Approach 1
    49         // This approach reads all types of known Jpeg metadata (at present,
    50         // Exif and Iptc) in a single call.  In most cases, this is the most
    51         // appropriate usage.
    52         try {
    53             Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);
    54             printImageTags(1, metadata);
    55         } catch (JpegProcessingException e) {
    56             System.err.println("error 1a");
    57         }
    58 
    59         // Approach 2
    60         // This approach shows using individual MetadataReader implementations
    61         // to read a file.  This is less efficient than approach 1, as the file
    62         // is opened and read twice.
    63         try {
    64             Metadata metadata = new Metadata();
    65             new ExifReader(jpegFile).extract(metadata);
    66             new IptcReader(jpegFile).extract(metadata);
    67             printImageTags(2, metadata);
    68         } catch (JpegProcessingException jpe) {
    69             System.err.println("error 2a");
    70         }
    71 
    72         // Approach 3
    73         // As fast as approach 1 (this is what goes on inside the JpegMetadataReader's
    74         // readMetadata() method), this code is handy if you want to look into other
    75         // Jpeg segments too.
    76         try {
    77             JpegSegmentReader segmentReader = new JpegSegmentReader(jpegFile);
    78             byte[] exifSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APP1);
    79             byte[] iptcSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APPD);
    80             Metadata metadata = new Metadata();
    81             new ExifReader(exifSegment).extract(metadata);
    82             new IptcReader(iptcSegment).extract(metadata);
    83             printImageTags(3, metadata);
    84         } catch (JpegProcessingException jpe) {
    85             System.err.println("error 3a");
    86         }
    87     }
    88 
    89     private void printImageTags(int approachCount, Metadata metadata)
    90     {
    91         System.out.println();
    92         System.out.println("*** APPROACH " + approachCount + " ***");
    93         System.out.println();
    94         // iterate over the exif data and print to System.out
    95         Iterator directories = metadata.getDirectoryIterator();
    96         while (directories.hasNext()) {
    97             Directory directory = (Directory)directories.next();
    98             Iterator tags = directory.getTagIterator();
    99             while (tags.hasNext()) {
    100                 Tag tag = (Tag)tags.next();
    101                 System.out.println(tag);
    102             }
    103             if (directory.hasErrors()) {
    104                 Iterator errors = directory.getErrors();
    105                 while (errors.hasNext()) {
    106                     System.out.println("ERROR: " + errors.next());
    107                 }
    108             }
    109         }
    110     }
    111 
    112     /**
    113      * Executes the sample usage program.
    114      * @param args command line parameters
    115      */
    116     public static void main(String[] args)
    117     {
    118         new SampleUsage("src/com/drew/metadata/test/withIptcExifGps.jpg");
    119     }
    120 }
  • src/com/drew/metadata/Tag.java

     
    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;
    3241        _directory = directory;
     
    3443
    3544    /**
    3645     * Gets the tag type as an int
     46     *
    3747     * @return the tag type as an int
    3848     */
    3949    public int getTagType()
     
    4454    /**
    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    {
    5163        String hex = Integer.toHexString(_tagType);
     
    5668    /**
    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);
    6478    }
     
    6680    /**
    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    {
    7389        return _directory.getTagName(_tagType);
     
    7692    /**
    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    {
    83101        return _directory.getName();
    84102    }
    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    }
    101117}
  • src/com/drew/metadata/TagDescriptor.java

     
    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;
    1825
     26import java.lang.reflect.Array;
     27
    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;
    3143    }
     
    3345    /**
    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}
  • src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

     
    6868import com.drew.metadata.Directory;
    6969import com.drew.metadata.Metadata;
    7070import com.drew.metadata.MetadataException;
    71 import com.drew.metadata.exif.ExifDirectory;
     71import com.drew.metadata.exif.ExifIFD0Directory;
    7272import com.drew.metadata.exif.GpsDirectory;
    7373
    7474public class GeoImageLayer extends Layer implements PropertyChangeListener, JumpToMarkerLayer {
     
    510510        double deg;
    511511        double min, sec;
    512512        double lon, lat;
    513         Metadata metadata = null;
    514         Directory dirExif = null, dirGps = null;
     513        Metadata metadata;
     514        Directory dirExif;
     515        GpsDirectory dirGps;
    515516
    516517        try {
    517518            metadata = JpegMetadataReader.readMetadata(e.getFile());
    518             dirExif = metadata.getDirectory(ExifDirectory.class);
     519            dirExif = metadata.getDirectory(ExifIFD0Directory.class);
    519520            dirGps = metadata.getDirectory(GpsDirectory.class);
    520521        } catch (CompoundException p) {
    521522            e.setExifCoor(null);
    522523            e.setPos(null);
    523524            return;
     525        } catch (IOException p) {
     526            e.setExifCoor(null);
     527            e.setPos(null);
     528            return;
    524529        }
    525530
    526531        try {
    527             int orientation = dirExif.getInt(ExifDirectory.TAG_ORIENTATION);
     532            int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    528533            e.setExifOrientation(orientation);
    529534        } catch (MetadataException ex) {
    530535        }
     
    543548            // longitude
    544549
    545550            Rational[] components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
     551            if (components != null) {
     552                deg = components[0].doubleValue();
     553                min = components[1].doubleValue();
     554                sec = components[2].doubleValue();
    546555
    547             deg = components[0].doubleValue();
    548             min = components[1].doubleValue();
    549             sec = components[2].doubleValue();
     556                if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
     557                    throw new IllegalArgumentException();
    550558
    551             if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
    552                 throw new IllegalArgumentException();
     559                lon = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
    553560
    554             lon = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
    555 
    556             if (dirGps.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') {
    557                 lon = -lon;
     561                if (dirGps.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') {
     562                    lon = -lon;
     563                }
     564            } else {
     565                // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)
     566                lon = dirGps.getDouble(GpsDirectory.TAG_GPS_LONGITUDE);
    558567            }
    559568
    560569            // latitude
    561570
    562571            components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
     572            if (components != null) {
     573                deg = components[0].doubleValue();
     574                min = components[1].doubleValue();
     575                sec = components[2].doubleValue();
    563576
    564             deg = components[0].doubleValue();
    565             min = components[1].doubleValue();
    566             sec = components[2].doubleValue();
     577                if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
     578                    throw new IllegalArgumentException();
    567579
    568             if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec))
    569                 throw new IllegalArgumentException();
     580                lat = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
    570581
    571             lat = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600)));
     582                if (Double.isNaN(lat))
     583                    throw new IllegalArgumentException();
    572584
    573             if (Double.isNaN(lat))
    574                 throw new IllegalArgumentException();
    575 
    576             if (dirGps.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S') {
    577                 lat = -lat;
     585                if (dirGps.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S') {
     586                    lat = -lat;
     587                }
     588            } else {
     589                lat = dirGps.getDouble(GpsDirectory.TAG_GPS_LATITUDE);
    578590            }
    579591
    580592            // Store values
     
    582594            e.setExifCoor(new LatLon(lat, lon));
    583595            e.setPos(e.getExifCoor());
    584596
    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));
    596                 e.setPos(e.getExifCoor());
    597             } catch (CompoundException ex) {
    598                 e.setExifCoor(null);
    599                 e.setPos(null);
    600             }
    601597        } catch (Exception ex) { // (other exceptions, e.g. #5271)
    602598            System.err.println("Error reading EXIF from file: "+ex);
    603599            e.setExifCoor(null);
  • src/org/openstreetmap/josm/tools/ExifReader.java

     
    22package org.openstreetmap.josm.tools;
    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;
    1010import com.drew.imaging.jpeg.JpegProcessingException;
     
    1212import com.drew.metadata.Metadata;
    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/**
    1819 * Read out exif file information from a jpeg file
     
    2526            Metadata metadata = JpegMetadataReader.readMetadata(filename);
    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                    }
    3939                }
     
    5454        Integer orientation = null;
    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) {
    6262            e.printStackTrace();
     63        } catch (IOException e) {
     64            e.printStackTrace();
    6365        }
    6466        return orientation;
    6567    }