Changeset 8243 in josm


Ignore:
Timestamp:
2015-04-21T00:42:50+02:00 (9 years ago)
Author:
Don-vip
Message:

fix #11359 - update to metadata-extractor 2.8.1

Location:
trunk
Files:
7 added
31 edited

Legend:

Unmodified
Added
Removed
  • trunk/.classpath

    r8173 r8243  
    1414        <classpathentry kind="lib" path="test/lib/fest/MRJToolkitStubs-1.0.jar"/>
    1515        <classpathentry kind="lib" path="test/lib/jfcunit.jar"/>
    16         <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.7.0_76"/>
     16        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.7.0_80"/>
    1717        <classpathentry exported="true" kind="con" path="GROOVY_SUPPORT"/>
    1818        <classpathentry kind="lib" path="test/lib/unitils-core/commons-collections-3.2.jar"/>
  • trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java

    r8132 r8243  
    3535//import com.drew.metadata.adobe.AdobeJpegReader;
    3636import com.drew.metadata.exif.ExifReader;
     37import com.drew.metadata.file.FileMetadataReader;
    3738//import com.drew.metadata.icc.IccReader;
    3839import com.drew.metadata.iptc.IptcReader;
     
    7980    public static Metadata readMetadata(@NotNull File file, @Nullable Iterable<JpegSegmentMetadataReader> readers) throws JpegProcessingException, IOException
    8081    {
    81         InputStream inputStream = null;
    82         try
    83         {
    84             inputStream = new FileInputStream(file);
    85             return readMetadata(inputStream, readers);
     82        InputStream inputStream = new FileInputStream(file);
     83        Metadata metadata;
     84        try {
     85            metadata = readMetadata(inputStream, readers);
    8686        } finally {
    87             if (inputStream != null)
    88                 inputStream.close();
     87            inputStream.close();
    8988        }
     89        new FileMetadataReader().read(file, metadata);
     90        return metadata;
    9091    }
    9192
     
    123124        for (JpegSegmentMetadataReader reader : readers) {
    124125            for (JpegSegmentType segmentType : reader.getSegmentTypes()) {
    125                 for (byte[] segmentBytes : segmentData.getSegments(segmentType)) {
    126                     if (reader.canProcess(segmentBytes, segmentType)) {
    127                         reader.extract(segmentBytes, metadata, segmentType);
    128                     }
    129                 }
     126                reader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType);
    130127            }
    131128        }
  • trunk/src/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java

    r8132 r8243  
    1616
    1717    /**
    18      * Gets a value indicating whether the supplied byte data can be processed by this reader. This is not a guarantee
    19      * that no errors will occur, but rather a best-effort indication of whether the parse is likely to succeed.
    20      * Implementations are expected to check things such as the opening bytes, data length, etc.
    21      */
    22     public boolean canProcess(@NotNull final byte[] segmentBytes, @NotNull final JpegSegmentType segmentType);
    23 
    24     /**
    25      * Extracts metadata from a JPEG segment's byte array and merges it into the specified {@link Metadata} object.
     18     * Extracts metadata from all instances of a particular JPEG segment type.
    2619     *
    27      * @param segmentBytes The byte array from which the metadata should be extracted.
     20     * @param segments A sequence of byte arrays from which the metadata should be extracted. These are in the order
     21     *                 encountered in the original file.
    2822     * @param metadata The {@link Metadata} object into which extracted values should be merged.
    2923     * @param segmentType The {@link JpegSegmentType} being read.
    3024     */
    31     public void extract(@NotNull final byte[] segmentBytes, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType);
     25    public void readJpegSegments(@NotNull final Iterable<byte[]> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType);
    3226}
  • trunk/src/com/drew/imaging/tiff/TiffHandler.java

    r8132 r8243  
    2929
    3030/**
     31 * Interface of an class capable of handling events raised during the reading of a TIFF file
     32 * via {@link TiffReader}.
     33 *
    3134 * @author Drew Noakes https://drewnoakes.com
    3235 */
  • trunk/src/com/drew/lang/RandomAccessReader.java

    r8132 r8243  
    127127
    128128    /**
     129     * Gets whether a bit at a specific index is set or not.
     130     *
     131     * @param index the number of bits at which to test
     132     * @return true if the bit is set, otherwise false
     133     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
     134     */
     135    public boolean getBit(int index) throws IOException
     136    {
     137        int byteIndex = index / 8;
     138        int bitIndex = index % 8;
     139
     140        validateIndex(byteIndex, 1);
     141
     142        byte b = getByte(byteIndex);
     143        return ((b >> bitIndex) & 1) == 1;
     144    }
     145
     146    /**
    129147     * Returns an unsigned 8-bit int calculated from one byte of data at the specified index.
    130148     *
     
    195213            return (short) (((short)getByte(index + 1) << 8 & (short)0xFF00) |
    196214                            ((short)getByte(index    )      & (short)0xFF));
     215        }
     216    }
     217
     218    /**
     219     * Get a 24-bit unsigned integer from the buffer, returning it as an int.
     220     *
     221     * @param index position within the data buffer to read first byte
     222     * @return the unsigned 24-bit int value as a long, between 0x00000000 and 0x00FFFFFF
     223     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
     224     */
     225    public int getInt24(int index) throws IOException
     226    {
     227        validateIndex(index, 3);
     228
     229        if (_isMotorolaByteOrder) {
     230            // Motorola - MSB first (big endian)
     231            return (((int)getByte(index    )) << 16 & 0xFF0000) |
     232                   (((int)getByte(index + 1)) << 8  & 0xFF00) |
     233                   (((int)getByte(index + 2))       & 0xFF);
     234        } else {
     235            // Intel ordering - LSB first (little endian)
     236            return (((int)getByte(index + 2)) << 16 & 0xFF0000) |
     237                   (((int)getByte(index + 1)) << 8  & 0xFF00) |
     238                   (((int)getByte(index    ))       & 0xFF);
    197239        }
    198240    }
  • trunk/src/com/drew/lang/SequentialByteArrayReader.java

    r8132 r8243  
    3737    private int _index;
    3838
     39    public SequentialByteArrayReader(@NotNull byte[] bytes)
     40    {
     41        this(bytes, 0);
     42    }
     43
    3944    @SuppressWarnings("ConstantConditions")
    40     public SequentialByteArrayReader(@NotNull byte[] bytes)
     45    public SequentialByteArrayReader(@NotNull byte[] bytes, int baseIndex)
    4146    {
    4247        if (bytes == null)
     
    4449
    4550        _bytes = bytes;
    46         _index = 0;
     51        _index = baseIndex;
    4752    }
    4853
  • trunk/src/com/drew/metadata/Directory.java

    r8132 r8243  
    8181
    8282// VARIOUS METHODS
     83
     84    /**
     85     * Gets a value indicating whether the directory is empty, meaning it contains no errors and no tag values.
     86     */
     87    public boolean isEmpty()
     88    {
     89        return _errorList.isEmpty() && _definedTagList.isEmpty();
     90    }
    8391
    8492    /**
     
    759767                    if (timeZone != null)
    760768                        parser.setTimeZone(timeZone);
     769                    else
     770                        parser.setTimeZone(TimeZone.getTimeZone("GMT")); // don't interpret zone time
     771
    761772                    return parser.parse(dateString);
    762773                } catch (ParseException ex) {
  • trunk/src/com/drew/metadata/Metadata.java

    r8132 r8243  
    3737{
    3838    @NotNull
    39     private final Map<Class<? extends Directory>,Directory> _directoryByClass = new HashMap<Class<? extends Directory>, Directory>();
    40 
    41     /**
    42      * List of Directory objects set against this object.  Keeping a list handy makes
    43      * creation of an Iterator and counting tags simple.
    44      */
    45     @NotNull
    46     private final Collection<Directory> _directoryList = new ArrayList<Directory>();
    47 
    48     /**
    49      * Returns an objects for iterating over Directory objects in the order in which they were added.
    50      *
    51      * @return an iterable collection of directories
     39    private final Map<Class<? extends Directory>,Collection<Directory>> _directoryListByClass = new HashMap<Class<? extends Directory>, Collection<Directory>>();
     40
     41    /**
     42     * Returns an iterable set of the {@link Directory} instances contained in this metadata collection.
     43     *
     44     * @return an iterable set of directories
    5245     */
    5346    @NotNull
    5447    public Iterable<Directory> getDirectories()
    5548    {
    56         return Collections.unmodifiableCollection(_directoryList);
    57     }
    58 
    59     /**
    60      * Returns a count of unique directories in this metadata collection.
     49        return new DirectoryIterable(_directoryListByClass);
     50    }
     51
     52    @Nullable
     53    public <T extends Directory> Collection<T> getDirectoriesOfType(Class<T> type)
     54    {
     55        return (Collection<T>)_directoryListByClass.get(type);
     56    }
     57
     58    /**
     59     * Returns the count of directories in this metadata collection.
    6160     *
    6261     * @return the number of unique directory types set for this metadata collection
     
    6463    public int getDirectoryCount()
    6564    {
    66         return _directoryList.size();
    67     }
    68 
    69     /**
    70      * Returns a {@link Directory} of specified type.  If this {@link Metadata} object already contains
    71      * such a directory, it is returned.  Otherwise a new instance of this directory will be created and stored within
    72      * this {@link Metadata} object.
    73      *
    74      * @param type the type of the Directory implementation required.
    75      * @return a directory of the specified type.
    76      */
    77     @NotNull
     65        int count = 0;
     66        for (Map.Entry<Class<? extends Directory>,Collection<Directory>> pair : _directoryListByClass.entrySet())
     67            count += pair.getValue().size();
     68        return count;
     69    }
     70
     71    /**
     72     * Adds a directory to this metadata collection.
     73     *
     74     * @param directory the {@link Directory} to add into this metadata collection.
     75     */
     76    public <T extends Directory> void addDirectory(@NotNull T directory)
     77    {
     78        getOrCreateDirectoryList(directory.getClass()).add(directory);
     79    }
     80
     81    /**
     82     * Gets the first {@link Directory} of the specified type contained within this metadata collection.
     83     * If no instances of this type are present, <code>null</code> is returned.
     84     *
     85     * @param type the Directory type
     86     * @param <T> the Directory type
     87     * @return the first Directory of type T in this metadata collection, or <code>null</code> if none exist
     88     */
     89    @Nullable
    7890    @SuppressWarnings("unchecked")
    79     public <T extends Directory> T getOrCreateDirectory(@NotNull Class<T> type)
     91    public <T extends Directory> T getFirstDirectoryOfType(@NotNull Class<T> type)
    8092    {
    8193        // We suppress the warning here as the code asserts a map signature of Class<T>,T.
    8294        // So after get(Class<T>) it is for sure the result is from type T.
    8395
    84         // check if we've already issued this type of directory
    85         if (_directoryByClass.containsKey(type))
    86             return (T)_directoryByClass.get(type);
    87 
    88         T directory;
    89         try {
    90             directory = type.newInstance();
    91         } catch (Exception e) {
    92             throw new RuntimeException("Cannot instantiate provided Directory type: " + type.toString());
    93         }
    94         // store the directory
    95         _directoryByClass.put(type, directory);
    96         _directoryList.add(directory);
    97 
    98         return directory;
    99     }
    100 
    101     /**
    102      * If this {@link Metadata} object contains a {@link Directory} of the specified type, it is returned.
    103      * Otherwise <code>null</code> is returned.
    104      *
    105      * @param type the Directory type
    106      * @param <T> the Directory type
    107      * @return a Directory of type T if it exists in this {@link Metadata} object, otherwise <code>null</code>.
    108      */
    109     @Nullable
    110     @SuppressWarnings("unchecked")
    111     public <T extends Directory> T getDirectory(@NotNull Class<T> type)
    112     {
    113         // We suppress the warning here as the code asserts a map signature of Class<T>,T.
    114         // So after get(Class<T>) it is for sure the result is from type T.
    115 
    116         return (T)_directoryByClass.get(type);
    117     }
    118 
    119     /**
    120      * Indicates whether a given directory type has been created in this metadata
    121      * repository.  Directories are created by calling {@link Metadata#getOrCreateDirectory(Class)}.
     96        Collection<Directory> list = getDirectoryList(type);
     97
     98        if (list == null || list.isEmpty())
     99            return null;
     100
     101        return (T)list.iterator().next();
     102    }
     103
     104    /**
     105     * Indicates whether an instance of the given directory type exists in this Metadata instance.
    122106     *
    123107     * @param type the {@link Directory} type
    124      * @return true if the {@link Directory} has been created
    125      */
    126     public boolean containsDirectory(Class<? extends Directory> type)
    127     {
    128         return _directoryByClass.containsKey(type);
     108     * @return <code>true</code> if a {@link Directory} of the specified type exists, otherwise <code>false</code>
     109     */
     110    public boolean containsDirectoryOfType(Class<? extends Directory> type)
     111    {
     112        Collection<Directory> list = getDirectoryList(type);
     113        return list != null && !list.isEmpty();
    129114    }
    130115
     
    137122    public boolean hasErrors()
    138123    {
    139         for (Directory directory : _directoryList) {
     124        for (Directory directory : getDirectories()) {
    140125            if (directory.hasErrors())
    141126                return true;
     
    147132    public String toString()
    148133    {
     134        int count = getDirectoryCount();
    149135        return String.format("Metadata (%d %s)",
    150             _directoryList.size(),
    151             _directoryList.size() == 1
     136            count,
     137            count == 1
    152138                ? "directory"
    153139                : "directories");
    154140    }
     141
     142    @Nullable
     143    private <T extends Directory> Collection<Directory> getDirectoryList(@NotNull Class<T> type)
     144    {
     145        return _directoryListByClass.get(type);
     146    }
     147
     148    @NotNull
     149    private <T extends Directory> Collection<Directory> getOrCreateDirectoryList(@NotNull Class<T> type)
     150    {
     151        Collection<Directory> collection = getDirectoryList(type);
     152        if (collection != null)
     153            return collection;
     154        collection = new ArrayList<Directory>();
     155        _directoryListByClass.put(type, collection);
     156        return collection;
     157    }
     158
     159    private static class DirectoryIterable implements Iterable<Directory>
     160    {
     161        private final Map<Class<? extends Directory>, Collection<Directory>> _map;
     162
     163        public DirectoryIterable(Map<Class<? extends Directory>, Collection<Directory>> map)
     164        {
     165            _map = map;
     166        }
     167
     168        public Iterator<Directory> iterator()
     169        {
     170            return new DirectoryIterator(_map);
     171        }
     172
     173        private static class DirectoryIterator implements Iterator<Directory>
     174        {
     175            @NotNull
     176            private final Iterator<Map.Entry<Class<? extends Directory>, Collection<Directory>>> _mapIterator;
     177            @Nullable
     178            private Iterator<Directory> _listIterator;
     179
     180            public DirectoryIterator(Map<Class<? extends Directory>, Collection<Directory>> map)
     181            {
     182                _mapIterator = map.entrySet().iterator();
     183
     184                if (_mapIterator.hasNext())
     185                    _listIterator = _mapIterator.next().getValue().iterator();
     186            }
     187
     188            public boolean hasNext()
     189            {
     190                return _listIterator != null && (_listIterator.hasNext() || _mapIterator.hasNext());
     191            }
     192
     193            public Directory next()
     194            {
     195                if (_listIterator == null || (!_listIterator.hasNext() && !_mapIterator.hasNext()))
     196                    throw new NoSuchElementException();
     197
     198                while (!_listIterator.hasNext())
     199                    _listIterator = _mapIterator.next().getValue().iterator();
     200
     201                return _listIterator.next();
     202            }
     203
     204            public void remove()
     205            {
     206                throw new UnsupportedOperationException();
     207            }
     208        }
     209    }
    155210}
  • trunk/src/com/drew/metadata/exif/ExifIFD0Descriptor.java

    r8132 r8243  
    2222package com.drew.metadata.exif;
    2323
    24 import com.drew.lang.Rational;
    2524import com.drew.lang.annotations.NotNull;
    26 import com.drew.lang.annotations.Nullable;
    27 import com.drew.metadata.TagDescriptor;
    28 
    29 import java.io.UnsupportedEncodingException;
    30 
    31 import static com.drew.metadata.exif.ExifIFD0Directory.*;
    3225
    3326/**
     
    3629 * @author Drew Noakes https://drewnoakes.com
    3730 */
    38 public class ExifIFD0Descriptor extends TagDescriptor<ExifIFD0Directory>
     31public class ExifIFD0Descriptor extends ExifDescriptorBase<ExifIFD0Directory>
    3932{
    40     /**
    41      * Dictates whether rational values will be represented in decimal format in instances
    42      * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
    43      */
    44     private final boolean _allowDecimalRepresentationOfRationals = true;
    45 
    4633    public ExifIFD0Descriptor(@NotNull ExifIFD0Directory directory)
    4734    {
    4835        super(directory);
    4936    }
    50 
    51     // Note for the potential addition of brightness presentation in eV:
    52     // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv),
    53     // you must add SensitivityValue(Sv).
    54     // Ev=BV+Sv   Sv=log2(ISOSpeedRating/3.125)
    55     // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
    56 
    57     /**
    58      * Returns a descriptive value of the specified tag for this image.
    59      * Where possible, known values will be substituted here in place of the raw
    60      * tokens actually kept in the Exif segment.  If no substitution is
    61      * available, the value provided by getString(int) will be returned.
    62      * @param tagType the tag to find a description for
    63      * @return a description of the image's value for the specified tag, or
    64      *         <code>null</code> if the tag hasn't been defined.
    65      */
    66     @Override
    67     @Nullable
    68     public String getDescription(int tagType)
    69     {
    70         switch (tagType) {
    71             case TAG_RESOLUTION_UNIT:
    72                 return getResolutionDescription();
    73             case TAG_YCBCR_POSITIONING:
    74                 return getYCbCrPositioningDescription();
    75             case TAG_X_RESOLUTION:
    76                 return getXResolutionDescription();
    77             case TAG_Y_RESOLUTION:
    78                 return getYResolutionDescription();
    79             case TAG_REFERENCE_BLACK_WHITE:
    80                 return getReferenceBlackWhiteDescription();
    81             case TAG_ORIENTATION:
    82                 return getOrientationDescription();
    83 
    84             case TAG_WIN_AUTHOR:
    85                return getWindowsAuthorDescription();
    86             case TAG_WIN_COMMENT:
    87                return getWindowsCommentDescription();
    88             case TAG_WIN_KEYWORDS:
    89                return getWindowsKeywordsDescription();
    90             case TAG_WIN_SUBJECT:
    91                return getWindowsSubjectDescription();
    92             case TAG_WIN_TITLE:
    93                return getWindowsTitleDescription();
    94 
    95             default:
    96                 return super.getDescription(tagType);
    97         }
    98     }
    99 
    100     @Nullable
    101     public String getReferenceBlackWhiteDescription()
    102     {
    103         int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE);
    104         if (ints==null || ints.length < 6)
    105             return null;
    106         int blackR = ints[0];
    107         int whiteR = ints[1];
    108         int blackG = ints[2];
    109         int whiteG = ints[3];
    110         int blackB = ints[4];
    111         int whiteB = ints[5];
    112         return String.format("[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB);
    113     }
    114 
    115     @Nullable
    116     public String getYResolutionDescription()
    117     {
    118         Rational value = _directory.getRational(TAG_Y_RESOLUTION);
    119         if (value==null)
    120             return null;
    121         final String unit = getResolutionDescription();
    122         return String.format("%s dots per %s",
    123             value.toSimpleString(_allowDecimalRepresentationOfRationals),
    124             unit == null ? "unit" : unit.toLowerCase());
    125     }
    126 
    127     @Nullable
    128     public String getXResolutionDescription()
    129     {
    130         Rational value = _directory.getRational(TAG_X_RESOLUTION);
    131         if (value==null)
    132             return null;
    133         final String unit = getResolutionDescription();
    134         return String.format("%s dots per %s",
    135             value.toSimpleString(_allowDecimalRepresentationOfRationals),
    136             unit == null ? "unit" : unit.toLowerCase());
    137     }
    138 
    139     @Nullable
    140     public String getYCbCrPositioningDescription()
    141     {
    142         return getIndexedDescription(TAG_YCBCR_POSITIONING, 1, "Center of pixel array", "Datum point");
    143     }
    144 
    145     @Nullable
    146     public String getOrientationDescription()
    147     {
    148         return getIndexedDescription(TAG_ORIENTATION, 1,
    149             "Top, left side (Horizontal / normal)",
    150             "Top, right side (Mirror horizontal)",
    151             "Bottom, right side (Rotate 180)",
    152             "Bottom, left side (Mirror vertical)",
    153             "Left side, top (Mirror horizontal and rotate 270 CW)",
    154             "Right side, top (Rotate 90 CW)",
    155             "Right side, bottom (Mirror horizontal and rotate 90 CW)",
    156             "Left side, bottom (Rotate 270 CW)");
    157     }
    158 
    159     @Nullable
    160     public String getResolutionDescription()
    161     {
    162         // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
    163         return getIndexedDescription(TAG_RESOLUTION_UNIT, 1, "(No unit)", "Inch", "cm");
    164     }
    165 
    166     /** The Windows specific tags uses plain Unicode. */
    167     @Nullable
    168     private String getUnicodeDescription(int tag)
    169     {
    170         byte[] bytes = _directory.getByteArray(tag);
    171         if (bytes == null)
    172             return null;
    173         try {
    174             // Decode the unicode string and trim the unicode zero "\0" from the end.
    175             return new String(bytes, "UTF-16LE").trim();
    176         } catch (UnsupportedEncodingException ex) {
    177             return null;
    178         }
    179     }
    180 
    181     @Nullable
    182     public String getWindowsAuthorDescription()
    183     {
    184        return getUnicodeDescription(TAG_WIN_AUTHOR);
    185     }
    186 
    187     @Nullable
    188     public String getWindowsCommentDescription()
    189     {
    190        return getUnicodeDescription(TAG_WIN_COMMENT);
    191     }
    192 
    193     @Nullable
    194     public String getWindowsKeywordsDescription()
    195     {
    196        return getUnicodeDescription(TAG_WIN_KEYWORDS);
    197     }
    198 
    199     @Nullable
    200     public String getWindowsTitleDescription()
    201     {
    202        return getUnicodeDescription(TAG_WIN_TITLE);
    203     }
    204 
    205     @Nullable
    206     public String getWindowsSubjectDescription()
    207     {
    208        return getUnicodeDescription(TAG_WIN_SUBJECT);
    209     }
    21037}
  • trunk/src/com/drew/metadata/exif/ExifIFD0Directory.java

    r8132 r8243  
    2323
    2424import com.drew.lang.annotations.NotNull;
    25 import com.drew.metadata.Directory;
    2625
    2726import java.util.HashMap;
     
    3231 * @author Drew Noakes https://drewnoakes.com
    3332 */
    34 public class ExifIFD0Directory extends Directory
     33public class ExifIFD0Directory extends ExifDirectoryBase
    3534{
    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 
    49     public static final int TAG_YCBCR_COEFFICIENTS = 0x0211;
    50     public static final int TAG_YCBCR_POSITIONING = 0x0213;
    51     public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214;
    52 
    53 
    5435    /** This tag is a pointer to the Exif SubIFD. */
    5536    public static final int TAG_EXIF_SUB_IFD_OFFSET = 0x8769;
     
    5839    public static final int TAG_GPS_INFO_OFFSET = 0x8825;
    5940
    60     public static final int TAG_COPYRIGHT = 0x8298;
    61 
    62     /** Non-standard, but in use. */
    63     public static final int TAG_TIME_ZONE_OFFSET = 0x882a;
    64 
    65     /** The image title, as used by Windows XP. */
    66     public static final int TAG_WIN_TITLE = 0x9C9B;
    67     /** The image comment, as used by Windows XP. */
    68     public static final int TAG_WIN_COMMENT = 0x9C9C;
    69     /** The image author, as used by Windows XP (called Artist in the Windows shell). */
    70     public static final int TAG_WIN_AUTHOR = 0x9C9D;
    71     /** The image keywords, as used by Windows XP. */
    72     public static final int TAG_WIN_KEYWORDS = 0x9C9E;
    73     /** The image subject, as used by Windows XP. */
    74     public static final int TAG_WIN_SUBJECT = 0x9C9F;
     41    public ExifIFD0Directory()
     42    {
     43        this.setDescriptor(new ExifIFD0Descriptor(this));
     44    }
    7545
    7646    @NotNull
     
    7949    static
    8050    {
    81         _tagNameMap.put(TAG_IMAGE_DESCRIPTION, "Image Description");
    82         _tagNameMap.put(TAG_MAKE, "Make");
    83         _tagNameMap.put(TAG_MODEL, "Model");
    84         _tagNameMap.put(TAG_ORIENTATION, "Orientation");
    85         _tagNameMap.put(TAG_X_RESOLUTION, "X Resolution");
    86         _tagNameMap.put(TAG_Y_RESOLUTION, "Y Resolution");
    87         _tagNameMap.put(TAG_RESOLUTION_UNIT, "Resolution Unit");
    88         _tagNameMap.put(TAG_SOFTWARE, "Software");
    89         _tagNameMap.put(TAG_DATETIME, "Date/Time");
    90         _tagNameMap.put(TAG_ARTIST, "Artist");
    91         _tagNameMap.put(TAG_WHITE_POINT, "White Point");
    92         _tagNameMap.put(TAG_PRIMARY_CHROMATICITIES, "Primary Chromaticities");
    93         _tagNameMap.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients");
    94         _tagNameMap.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning");
    95         _tagNameMap.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White");
    96 
    97         _tagNameMap.put(TAG_COPYRIGHT, "Copyright");
    98 
    99         _tagNameMap.put(TAG_TIME_ZONE_OFFSET, "Time Zone Offset");
    100 
    101         _tagNameMap.put(TAG_WIN_AUTHOR, "Windows XP Author");
    102         _tagNameMap.put(TAG_WIN_COMMENT, "Windows XP Comment");
    103         _tagNameMap.put(TAG_WIN_KEYWORDS, "Windows XP Keywords");
    104         _tagNameMap.put(TAG_WIN_SUBJECT, "Windows XP Subject");
    105         _tagNameMap.put(TAG_WIN_TITLE, "Windows XP Title");
    106     }
    107 
    108     public ExifIFD0Directory()
    109     {
    110         this.setDescriptor(new ExifIFD0Descriptor(this));
     51        addExifTagNames(_tagNameMap);
    11152    }
    11253
  • trunk/src/com/drew/metadata/exif/ExifInteropDescriptor.java

    r8132 r8243  
    2222
    2323import com.drew.lang.annotations.NotNull;
    24 import com.drew.lang.annotations.Nullable;
    25 import com.drew.metadata.TagDescriptor;
    26 
    27 import static com.drew.metadata.exif.ExifInteropDirectory.*;
    2824
    2925/**
     
    3228 * @author Drew Noakes https://drewnoakes.com
    3329 */
    34 public class ExifInteropDescriptor extends TagDescriptor<ExifInteropDirectory>
     30public class ExifInteropDescriptor extends ExifDescriptorBase<ExifInteropDirectory>
    3531{
    3632    public ExifInteropDescriptor(@NotNull ExifInteropDirectory directory)
     
    3834        super(directory);
    3935    }
    40 
    41     @Override
    42     @Nullable
    43     public String getDescription(int tagType)
    44     {
    45         switch (tagType) {
    46             case TAG_INTEROP_INDEX:
    47                 return getInteropIndexDescription();
    48             case TAG_INTEROP_VERSION:
    49                 return getInteropVersionDescription();
    50             default:
    51                 return super.getDescription(tagType);
    52         }
    53     }
    54 
    55     @Nullable
    56     public String getInteropVersionDescription()
    57     {
    58         return getVersionBytesDescription(TAG_INTEROP_VERSION, 2);
    59     }
    60 
    61     @Nullable
    62     public String getInteropIndexDescription()
    63     {
    64         String value = _directory.getString(TAG_INTEROP_INDEX);
    65 
    66         if (value == null)
    67             return null;
    68 
    69         return "R98".equalsIgnoreCase(value.trim())
    70                 ? "Recommended Exif Interoperability Rules (ExifR98)"
    71                 : "Unknown (" + value + ")";
    72     }
    7336}
  • trunk/src/com/drew/metadata/exif/ExifInteropDirectory.java

    r8132 r8243  
    2222
    2323import com.drew.lang.annotations.NotNull;
    24 import com.drew.metadata.Directory;
    2524
    2625import java.util.HashMap;
     
    3130 * @author Drew Noakes https://drewnoakes.com
    3231 */
    33 public class ExifInteropDirectory extends Directory
     32public class ExifInteropDirectory extends ExifDirectoryBase
    3433{
    35     public static final int TAG_INTEROP_INDEX = 0x0001;
    36     public static final int TAG_INTEROP_VERSION = 0x0002;
    37     public static final int TAG_RELATED_IMAGE_FILE_FORMAT = 0x1000;
    38     public static final int TAG_RELATED_IMAGE_WIDTH = 0x1001;
    39     public static final int TAG_RELATED_IMAGE_LENGTH = 0x1002;
    40 
    4134    @NotNull
    4235    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
     
    4437    static
    4538    {
    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");
     39        addExifTagNames(_tagNameMap);
    5140    }
    5241
  • trunk/src/com/drew/metadata/exif/ExifReader.java

    r8132 r8243  
    2626import com.drew.imaging.tiff.TiffReader;
    2727import com.drew.lang.ByteArrayReader;
     28import com.drew.lang.RandomAccessReader;
    2829import com.drew.lang.annotations.NotNull;
    2930import com.drew.metadata.Metadata;
     
    4142public class ExifReader implements JpegSegmentMetadataReader
    4243{
    43     /**
    44      * The offset at which the TIFF data actually starts. This may be necessary when, for example, processing
    45      * JPEG Exif data from APP0 which has a 6-byte preamble before starting the TIFF data.
    46      */
    47     private static final String JPEG_EXIF_SEGMENT_PREAMBLE = "Exif\0\0";
     44    /** Exif data stored in JPEG files' APP1 segment are preceded by this six character preamble. */
     45    public static final String JPEG_SEGMENT_PREAMBLE = "Exif\0\0";
    4846
    4947    private boolean _storeThumbnailBytes = true;
     
    6563    }
    6664
    67     public boolean canProcess(@NotNull final byte[] segmentBytes, @NotNull final JpegSegmentType segmentType)
     65    public void readJpegSegments(@NotNull final Iterable<byte[]> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType)
    6866    {
    69         return segmentBytes.length >= JPEG_EXIF_SEGMENT_PREAMBLE.length() && new String(segmentBytes, 0, JPEG_EXIF_SEGMENT_PREAMBLE.length()).equalsIgnoreCase(JPEG_EXIF_SEGMENT_PREAMBLE);
     67        assert(segmentType == JpegSegmentType.APP1);
     68
     69        for (byte[] segmentBytes : segments) {
     70            // Filter any segments containing unexpected preambles
     71            if (segmentBytes.length < JPEG_SEGMENT_PREAMBLE.length() || !new String(segmentBytes, 0, JPEG_SEGMENT_PREAMBLE.length()).equals(JPEG_SEGMENT_PREAMBLE))
     72                continue;
     73            extract(new ByteArrayReader(segmentBytes), metadata, JPEG_SEGMENT_PREAMBLE.length());
     74        }
    7075    }
    7176
    72     public void extract(@NotNull final byte[] segmentBytes, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType)
     77    /** Reads TIFF formatted Exif data from start of the specified {@link RandomAccessReader}. */
     78    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata)
    7379    {
    74         if (segmentBytes == null)
    75             throw new NullPointerException("segmentBytes cannot be null");
    76         if (metadata == null)
    77             throw new NullPointerException("metadata cannot be null");
    78         if (segmentType == null)
    79             throw new NullPointerException("segmentType cannot be null");
     80        extract(reader, metadata, 0);
     81    }
    8082
     83    /** Reads TIFF formatted Exif data a specified offset within a {@link RandomAccessReader}. */
     84    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset)
     85    {
    8186        try {
    82             ByteArrayReader reader = new ByteArrayReader(segmentBytes);
    83 
    84             //
    85             // Check for the header preamble
    86             //
    87             try {
    88                 if (!reader.getString(0, JPEG_EXIF_SEGMENT_PREAMBLE.length()).equals(JPEG_EXIF_SEGMENT_PREAMBLE)) {
    89                     // TODO what do to with this error state?
    90                     System.err.println("Invalid JPEG Exif segment preamble");
    91                     return;
    92                 }
    93             } catch (IOException e) {
    94                 // TODO what do to with this error state?
    95                 e.printStackTrace(System.err);
    96                 return;
    97             }
    98 
    99             //
    10087            // Read the TIFF-formatted Exif data
    101             //
    10288            new TiffReader().processTiff(
    10389                reader,
    10490                new ExifTiffHandler(metadata, _storeThumbnailBytes),
    105                 JPEG_EXIF_SEGMENT_PREAMBLE.length()
     91                readerOffset
    10692            );
    107 
    10893        } catch (TiffProcessingException e) {
    10994            // TODO what do to with this error state?
  • trunk/src/com/drew/metadata/exif/ExifSubIFDDescriptor.java

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

    r8132 r8243  
    2222
    2323import com.drew.lang.annotations.NotNull;
    24 import com.drew.metadata.Directory;
    2524
    2625import java.util.HashMap;
     
    3130 * @author Drew Noakes https://drewnoakes.com
    3231 */
    33 public class ExifSubIFDDirectory extends Directory
     32public class ExifSubIFDDirectory extends ExifDirectoryBase
    3433{
    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 
    263     /**
    264      * This tag holds the Exif Makernote. Makernotes are free to be in any format, though they are often IFDs.
    265      * To determine the format, we consider the starting bytes of the makernote itself and sometimes the
    266      * camera model and make.
    267      * <p>
    268      * The component count for this tag includes all of the bytes needed for the makernote.
    269      */
    270     public static final int TAG_MAKERNOTE = 0x927C;
    271 
    272     public static final int TAG_USER_COMMENT = 0x9286;
    273 
    274     public static final int TAG_SUBSECOND_TIME = 0x9290;
    275     public static final int TAG_SUBSECOND_TIME_ORIGINAL = 0x9291;
    276     public static final int TAG_SUBSECOND_TIME_DIGITIZED = 0x9292;
    277 
    278     public static final int TAG_FLASHPIX_VERSION = 0xA000;
    279     /**
    280      * Defines Color Space. DCF image must use sRGB color space so value is
    281      * always '1'. If the picture uses the other color space, value is
    282      * '65535':Uncalibrated.
    283      */
    284     public static final int TAG_COLOR_SPACE = 0xA001;
    285     public static final int TAG_EXIF_IMAGE_WIDTH = 0xA002;
    286     public static final int TAG_EXIF_IMAGE_HEIGHT = 0xA003;
    287     public static final int TAG_RELATED_SOUND_FILE = 0xA004;
    288 
    28934    /** This tag is a pointer to the Exif Interop IFD. */
    29035    public static final int TAG_INTEROP_OFFSET = 0xA005;
    29136
    292     public static final int TAG_FOCAL_PLANE_X_RESOLUTION = 0xA20E;
    293     public static final int TAG_FOCAL_PLANE_Y_RESOLUTION = 0xA20F;
    294     /**
    295      * Unit of FocalPlaneXResolution/FocalPlaneYResolution. '1' means no-unit,
    296      * '2' inch, '3' centimeter.
    297      *
    298      * Note: Some of Fujifilm's digicam(e.g.FX2700,FX2900,Finepix4700Z/40i etc)
    299      * uses value '3' so it must be 'centimeter', but it seems that they use a
    300      * '8.3mm?'(1/3in.?) to their ResolutionUnit. Fuji's BUG? Finepix4900Z has
    301      * been changed to use value '2' but it doesn't match to actual value also.
    302      */
    303     public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT = 0xA210;
    304     public static final int TAG_EXPOSURE_INDEX = 0xA215;
    305     public static final int TAG_SENSING_METHOD = 0xA217;
    306     public static final int TAG_FILE_SOURCE = 0xA300;
    307     public static final int TAG_SCENE_TYPE = 0xA301;
    308     public static final int TAG_CFA_PATTERN = 0xA302;
    309 
    310     // these tags new with Exif 2.2 (?) [A401 - A4
    311     /**
    312      * This tag indicates the use of special processing on image data, such as rendering
    313      * geared to output. When special processing is performed, the reader is expected to
    314      * disable or minimize any further processing.
    315      * Tag = 41985 (A401.H)
    316      * Type = SHORT
    317      * Count = 1
    318      * Default = 0
    319      *   0 = Normal process
    320      *   1 = Custom process
    321      *   Other = reserved
    322      */
    323     public static final int TAG_CUSTOM_RENDERED = 0xA401;
    324 
    325     /**
    326      * This tag indicates the exposure mode set when the image was shot. In auto-bracketing
    327      * mode, the camera shoots a series of frames of the same scene at different exposure settings.
    328      * Tag = 41986 (A402.H)
    329      * Type = SHORT
    330      * Count = 1
    331      * Default = none
    332      *   0 = Auto exposure
    333      *   1 = Manual exposure
    334      *   2 = Auto bracket
    335      *   Other = reserved
    336      */
    337     public static final int TAG_EXPOSURE_MODE = 0xA402;
    338 
    339     /**
    340      * This tag indicates the white balance mode set when the image was shot.
    341      * Tag = 41987 (A403.H)
    342      * Type = SHORT
    343      * Count = 1
    344      * Default = none
    345      *   0 = Auto white balance
    346      *   1 = Manual white balance
    347      *   Other = reserved
    348      */
    349     public static final int TAG_WHITE_BALANCE_MODE = 0xA403;
    350 
    351     /**
    352      * This tag indicates the digital zoom ratio when the image was shot. If the
    353      * numerator of the recorded value is 0, this indicates that digital zoom was
    354      * not used.
    355      * Tag = 41988 (A404.H)
    356      * Type = RATIONAL
    357      * Count = 1
    358      * Default = none
    359      */
    360     public static final int TAG_DIGITAL_ZOOM_RATIO = 0xA404;
    361 
    362     /**
    363      * This tag indicates the equivalent focal length assuming a 35mm film camera,
    364      * in mm. A value of 0 means the focal length is unknown. Note that this tag
    365      * differs from the FocalLength tag.
    366      * Tag = 41989 (A405.H)
    367      * Type = SHORT
    368      * Count = 1
    369      * Default = none
    370      */
    371     public static final int TAG_35MM_FILM_EQUIV_FOCAL_LENGTH = 0xA405;
    372 
    373     /**
    374      * This tag indicates the type of scene that was shot. It can also be used to
    375      * record the mode in which the image was shot. Note that this differs from
    376      * the scene type (SceneType) tag.
    377      * Tag = 41990 (A406.H)
    378      * Type = SHORT
    379      * Count = 1
    380      * Default = 0
    381      *   0 = Standard
    382      *   1 = Landscape
    383      *   2 = Portrait
    384      *   3 = Night scene
    385      *   Other = reserved
    386      */
    387     public static final int TAG_SCENE_CAPTURE_TYPE = 0xA406;
    388 
    389     /**
    390      * This tag indicates the degree of overall image gain adjustment.
    391      * Tag = 41991 (A407.H)
    392      * Type = SHORT
    393      * Count = 1
    394      * Default = none
    395      *   0 = None
    396      *   1 = Low gain up
    397      *   2 = High gain up
    398      *   3 = Low gain down
    399      *   4 = High gain down
    400      *   Other = reserved
    401      */
    402     public static final int TAG_GAIN_CONTROL = 0xA407;
    403 
    404     /**
    405      * This tag indicates the direction of contrast processing applied by the camera
    406      * when the image was shot.
    407      * Tag = 41992 (A408.H)
    408      * Type = SHORT
    409      * Count = 1
    410      * Default = 0
    411      *   0 = Normal
    412      *   1 = Soft
    413      *   2 = Hard
    414      *   Other = reserved
    415      */
    416     public static final int TAG_CONTRAST = 0xA408;
    417 
    418     /**
    419      * This tag indicates the direction of saturation processing applied by the camera
    420      * when the image was shot.
    421      * Tag = 41993 (A409.H)
    422      * Type = SHORT
    423      * Count = 1
    424      * Default = 0
    425      *   0 = Normal
    426      *   1 = Low saturation
    427      *   2 = High saturation
    428      *   Other = reserved
    429      */
    430     public static final int TAG_SATURATION = 0xA409;
    431 
    432     /**
    433      * This tag indicates the direction of sharpness processing applied by the camera
    434      * when the image was shot.
    435      * Tag = 41994 (A40A.H)
    436      * Type = SHORT
    437      * Count = 1
    438      * Default = 0
    439      *   0 = Normal
    440      *   1 = Soft
    441      *   2 = Hard
    442      *   Other = reserved
    443      */
    444     public static final int TAG_SHARPNESS = 0xA40A;
    445 
    446     // TODO support this tag (I haven't seen a camera's actual implementation of this yet)
    447 
    448     /**
    449      * This tag indicates information on the picture-taking conditions of a particular
    450      * camera model. The tag is used only to indicate the picture-taking conditions in
    451      * the reader.
    452      * Tag = 41995 (A40B.H)
    453      * Type = UNDEFINED
    454      * Count = Any
    455      * Default = none
    456      *
    457      * The information is recorded in the format shown below. The data is recorded
    458      * in Unicode using SHORT type for the number of display rows and columns and
    459      * UNDEFINED type for the camera settings. The Unicode (UCS-2) string including
    460      * Signature is NULL terminated. The specifics of the Unicode string are as given
    461      * in ISO/IEC 10464-1.
    462      *
    463      *      Length  Type        Meaning
    464      *      ------+-----------+------------------
    465      *      2       SHORT       Display columns
    466      *      2       SHORT       Display rows
    467      *      Any     UNDEFINED   Camera setting-1
    468      *      Any     UNDEFINED   Camera setting-2
    469      *      :       :           :
    470      *      Any     UNDEFINED   Camera setting-n
    471      */
    472     public static final int TAG_DEVICE_SETTING_DESCRIPTION = 0xA40B;
    473 
    474     /**
    475      * This tag indicates the distance to the subject.
    476      * Tag = 41996 (A40C.H)
    477      * Type = SHORT
    478      * Count = 1
    479      * Default = none
    480      *   0 = unknown
    481      *   1 = Macro
    482      *   2 = Close view
    483      *   3 = Distant view
    484      *   Other = reserved
    485      */
    486     public static final int TAG_SUBJECT_DISTANCE_RANGE = 0xA40C;
    487 
    488     /**
    489      * This tag indicates an identifier assigned uniquely to each image. It is
    490      * recorded as an ASCII string equivalent to hexadecimal notation and 128-bit
    491      * fixed length.
    492      * Tag = 42016 (A420.H)
    493      * Type = ASCII
    494      * Count = 33
    495      * Default = none
    496      */
    497     public static final int TAG_IMAGE_UNIQUE_ID = 0xA420;
    498 
    499     /** String. */
    500     public static final int TAG_CAMERA_OWNER_NAME = 0xA430;
    501     /** String. */
    502     public static final int TAG_BODY_SERIAL_NUMBER = 0xA431;
    503     /** An array of four Rational64u numbers giving focal and aperture ranges. */
    504     public static final int TAG_LENS_SPECIFICATION = 0xA432;
    505     /** String. */
    506     public static final int TAG_LENS_MAKE = 0xA433;
    507     /** String. */
    508     public static final int TAG_LENS_MODEL = 0xA434;
    509     /** String. */
    510     public static final int TAG_LENS_SERIAL_NUMBER = 0xA435;
    511     /** Rational64u. */
    512     public static final int TAG_GAMMA = 0xA500;
    513 
    514     public static final int TAG_LENS = 0xFDEA;
     37    public ExifSubIFDDirectory()
     38    {
     39        this.setDescriptor(new ExifSubIFDDescriptor(this));
     40    }
    51541
    51642    @NotNull
     
    51945    static
    52046    {
    521         _tagNameMap.put(TAG_FILL_ORDER, "Fill Order");
    522         _tagNameMap.put(TAG_DOCUMENT_NAME, "Document Name");
    523         // TODO why don't these tags have fields associated with them?
    524         _tagNameMap.put(0x1000, "Related Image File Format");
    525         _tagNameMap.put(0x1001, "Related Image Width");
    526         _tagNameMap.put(0x1002, "Related Image Length");
    527         _tagNameMap.put(0x0156, "Transfer Range");
    528         _tagNameMap.put(0x0200, "JPEG Proc");
    529         _tagNameMap.put(TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL, "Compressed Bits Per Pixel");
    530         _tagNameMap.put(TAG_MAKERNOTE, "Makernote");
    531         _tagNameMap.put(TAG_INTEROP_OFFSET, "Interoperability Offset");
    532 
    533         _tagNameMap.put(TAG_NEW_SUBFILE_TYPE, "New Subfile Type");
    534         _tagNameMap.put(TAG_SUBFILE_TYPE, "Subfile Type");
    535         _tagNameMap.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample");
    536         _tagNameMap.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation");
    537         _tagNameMap.put(TAG_THRESHOLDING, "Thresholding");
    538         _tagNameMap.put(TAG_STRIP_OFFSETS, "Strip Offsets");
    539         _tagNameMap.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel");
    540         _tagNameMap.put(TAG_ROWS_PER_STRIP, "Rows Per Strip");
    541         _tagNameMap.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts");
    542         _tagNameMap.put(TAG_PAGE_NAME, "Page Name");
    543         _tagNameMap.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration");
    544         _tagNameMap.put(TAG_TRANSFER_FUNCTION, "Transfer Function");
    545         _tagNameMap.put(TAG_PREDICTOR, "Predictor");
    546         _tagNameMap.put(TAG_TILE_WIDTH, "Tile Width");
    547         _tagNameMap.put(TAG_TILE_LENGTH, "Tile Length");
    548         _tagNameMap.put(TAG_TILE_OFFSETS, "Tile Offsets");
    549         _tagNameMap.put(TAG_TILE_BYTE_COUNTS, "Tile Byte Counts");
    550         _tagNameMap.put(TAG_JPEG_TABLES, "JPEG Tables");
    551         _tagNameMap.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling");
    552         _tagNameMap.put(TAG_CFA_REPEAT_PATTERN_DIM, "CFA Repeat Pattern Dim");
    553         _tagNameMap.put(TAG_CFA_PATTERN_2, "CFA Pattern");
    554         _tagNameMap.put(TAG_BATTERY_LEVEL, "Battery Level");
    555         _tagNameMap.put(TAG_EXPOSURE_TIME, "Exposure Time");
    556         _tagNameMap.put(TAG_FNUMBER, "F-Number");
    557         _tagNameMap.put(TAG_IPTC_NAA, "IPTC/NAA");
    558         _tagNameMap.put(TAG_INTER_COLOR_PROFILE, "Inter Color Profile");
    559         _tagNameMap.put(TAG_EXPOSURE_PROGRAM, "Exposure Program");
    560         _tagNameMap.put(TAG_SPECTRAL_SENSITIVITY, "Spectral Sensitivity");
    561         _tagNameMap.put(TAG_ISO_EQUIVALENT, "ISO Speed Ratings");
    562         _tagNameMap.put(TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION, "Opto-electric Conversion Function (OECF)");
    563         _tagNameMap.put(TAG_INTERLACE, "Interlace");
    564         _tagNameMap.put(TAG_TIME_ZONE_OFFSET, "Time Zone Offset");
    565         _tagNameMap.put(TAG_SELF_TIMER_MODE, "Self Timer Mode");
    566         _tagNameMap.put(TAG_EXIF_VERSION, "Exif Version");
    567         _tagNameMap.put(TAG_DATETIME_ORIGINAL, "Date/Time Original");
    568         _tagNameMap.put(TAG_DATETIME_DIGITIZED, "Date/Time Digitized");
    569         _tagNameMap.put(TAG_COMPONENTS_CONFIGURATION, "Components Configuration");
    570         _tagNameMap.put(TAG_SHUTTER_SPEED, "Shutter Speed Value");
    571         _tagNameMap.put(TAG_APERTURE, "Aperture Value");
    572         _tagNameMap.put(TAG_BRIGHTNESS_VALUE, "Brightness Value");
    573         _tagNameMap.put(TAG_EXPOSURE_BIAS, "Exposure Bias Value");
    574         _tagNameMap.put(TAG_MAX_APERTURE, "Max Aperture Value");
    575         _tagNameMap.put(TAG_SUBJECT_DISTANCE, "Subject Distance");
    576         _tagNameMap.put(TAG_METERING_MODE, "Metering Mode");
    577         _tagNameMap.put(TAG_LIGHT_SOURCE, "Light Source");
    578         _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
    579         _tagNameMap.put(TAG_FLASH, "Flash");
    580         _tagNameMap.put(TAG_FOCAL_LENGTH, "Focal Length");
    581         _tagNameMap.put(TAG_FLASH_ENERGY, "Flash Energy");
    582         _tagNameMap.put(TAG_SPATIAL_FREQ_RESPONSE, "Spatial Frequency Response");
    583         _tagNameMap.put(TAG_NOISE, "Noise");
    584         _tagNameMap.put(TAG_IMAGE_NUMBER, "Image Number");
    585         _tagNameMap.put(TAG_SECURITY_CLASSIFICATION, "Security Classification");
    586         _tagNameMap.put(TAG_IMAGE_HISTORY, "Image History");
    587         _tagNameMap.put(TAG_SUBJECT_LOCATION, "Subject Location");
    588         _tagNameMap.put(TAG_EXPOSURE_INDEX, "Exposure Index");
    589         _tagNameMap.put(TAG_TIFF_EP_STANDARD_ID, "TIFF/EP Standard ID");
    590         _tagNameMap.put(TAG_USER_COMMENT, "User Comment");
    591         _tagNameMap.put(TAG_SUBSECOND_TIME, "Sub-Sec Time");
    592         _tagNameMap.put(TAG_SUBSECOND_TIME_ORIGINAL, "Sub-Sec Time Original");
    593         _tagNameMap.put(TAG_SUBSECOND_TIME_DIGITIZED, "Sub-Sec Time Digitized");
    594         _tagNameMap.put(TAG_FLASHPIX_VERSION, "FlashPix Version");
    595         _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
    596         _tagNameMap.put(TAG_EXIF_IMAGE_WIDTH, "Exif Image Width");
    597         _tagNameMap.put(TAG_EXIF_IMAGE_HEIGHT, "Exif Image Height");
    598         _tagNameMap.put(TAG_RELATED_SOUND_FILE, "Related Sound File");
    599         // 0x920B in TIFF/EP
    600         _tagNameMap.put(TAG_FLASH_ENERGY_2, "Flash Energy");
    601         // 0x920C in TIFF/EP
    602         _tagNameMap.put(TAG_SPATIAL_FREQ_RESPONSE_2, "Spatial Frequency Response");
    603         // 0x920E in TIFF/EP
    604         _tagNameMap.put(TAG_FOCAL_PLANE_X_RESOLUTION, "Focal Plane X Resolution");
    605         // 0x920F in TIFF/EP
    606         _tagNameMap.put(TAG_FOCAL_PLANE_Y_RESOLUTION, "Focal Plane Y Resolution");
    607         // 0x9210 in TIFF/EP
    608         _tagNameMap.put(TAG_FOCAL_PLANE_RESOLUTION_UNIT, "Focal Plane Resolution Unit");
    609         // 0x9214 in TIFF/EP
    610         _tagNameMap.put(TAG_SUBJECT_LOCATION_2, "Subject Location");
    611         // 0x9215 in TIFF/EP
    612         _tagNameMap.put(TAG_EXPOSURE_INDEX_2, "Exposure Index");
    613         // 0x9217 in TIFF/EP
    614         _tagNameMap.put(TAG_SENSING_METHOD, "Sensing Method");
    615         _tagNameMap.put(TAG_FILE_SOURCE, "File Source");
    616         _tagNameMap.put(TAG_SCENE_TYPE, "Scene Type");
    617         _tagNameMap.put(TAG_CFA_PATTERN, "CFA Pattern");
    618 
    619         _tagNameMap.put(TAG_CUSTOM_RENDERED, "Custom Rendered");
    620         _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode");
    621         _tagNameMap.put(TAG_WHITE_BALANCE_MODE, "White Balance Mode");
    622         _tagNameMap.put(TAG_DIGITAL_ZOOM_RATIO, "Digital Zoom Ratio");
    623         _tagNameMap.put(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH, "Focal Length 35");
    624         _tagNameMap.put(TAG_SCENE_CAPTURE_TYPE, "Scene Capture Type");
    625         _tagNameMap.put(TAG_GAIN_CONTROL, "Gain Control");
    626         _tagNameMap.put(TAG_CONTRAST, "Contrast");
    627         _tagNameMap.put(TAG_SATURATION, "Saturation");
    628         _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
    629         _tagNameMap.put(TAG_DEVICE_SETTING_DESCRIPTION, "Device Setting Description");
    630         _tagNameMap.put(TAG_SUBJECT_DISTANCE_RANGE, "Subject Distance Range");
    631         _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Unique Image ID");
    632 
    633         _tagNameMap.put(TAG_CAMERA_OWNER_NAME, "Camera Owner Name");
    634         _tagNameMap.put(TAG_BODY_SERIAL_NUMBER, "Body Serial Number");
    635         _tagNameMap.put(TAG_LENS_SPECIFICATION, "Lens Specification");
    636         _tagNameMap.put(TAG_LENS_MAKE, "Lens Make");
    637         _tagNameMap.put(TAG_LENS_MODEL, "Lens Model");
    638         _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
    639         _tagNameMap.put(TAG_GAMMA, "Gamma");
    640 
    641         _tagNameMap.put(TAG_MIN_SAMPLE_VALUE, "Minimum sample value");
    642         _tagNameMap.put(TAG_MAX_SAMPLE_VALUE, "Maximum sample value");
    643 
    644         _tagNameMap.put(TAG_LENS, "Lens");
    645     }
    646 
    647     public ExifSubIFDDirectory()
    648     {
    649         this.setDescriptor(new ExifSubIFDDescriptor(this));
     47        addExifTagNames(_tagNameMap);
    65048    }
    65149
  • trunk/src/com/drew/metadata/exif/ExifThumbnailDescriptor.java

    r8132 r8243  
    2222package com.drew.metadata.exif;
    2323
    24 import com.drew.lang.Rational;
    2524import com.drew.lang.annotations.NotNull;
    2625import com.drew.lang.annotations.Nullable;
    27 import com.drew.metadata.TagDescriptor;
    2826
    2927import static com.drew.metadata.exif.ExifThumbnailDirectory.*;
     
    3432 * @author Drew Noakes https://drewnoakes.com
    3533 */
    36 public class ExifThumbnailDescriptor extends TagDescriptor<ExifThumbnailDirectory>
     34public class ExifThumbnailDescriptor extends ExifDescriptorBase<ExifThumbnailDirectory>
    3735{
    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 
    4436    public ExifThumbnailDescriptor(@NotNull ExifThumbnailDirectory directory)
    4537    {
     
    4739    }
    4840
    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 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      *
    61      * @param tagType the tag to find a description for
    62      * @return a description of the image's value for the specified tag, or
    63      *         <code>null</code> if the tag hasn't been defined.
    64      */
    6541    @Override
    6642    @Nullable
     
    6844    {
    6945        switch (tagType) {
    70             case TAG_ORIENTATION:
    71                 return getOrientationDescription();
    72             case TAG_RESOLUTION_UNIT:
    73                 return getResolutionDescription();
    74             case TAG_YCBCR_POSITIONING:
    75                 return getYCbCrPositioningDescription();
    76             case TAG_X_RESOLUTION:
    77                 return getXResolutionDescription();
    78             case TAG_Y_RESOLUTION:
    79                 return getYResolutionDescription();
    8046            case TAG_THUMBNAIL_OFFSET:
    8147                return getThumbnailOffsetDescription();
    8248            case TAG_THUMBNAIL_LENGTH:
    8349                return getThumbnailLengthDescription();
    84             case TAG_THUMBNAIL_IMAGE_WIDTH:
    85                 return getThumbnailImageWidthDescription();
    86             case TAG_THUMBNAIL_IMAGE_HEIGHT:
    87                 return getThumbnailImageHeightDescription();
    88             case TAG_BITS_PER_SAMPLE:
    89                 return getBitsPerSampleDescription();
    9050            case TAG_THUMBNAIL_COMPRESSION:
    9151                return getCompressionDescription();
    92             case TAG_PHOTOMETRIC_INTERPRETATION:
    93                 return getPhotometricInterpretationDescription();
    94             case TAG_ROWS_PER_STRIP:
    95                 return getRowsPerStripDescription();
    96             case TAG_STRIP_BYTE_COUNTS:
    97                 return getStripByteCountsDescription();
    98             case TAG_SAMPLES_PER_PIXEL:
    99                 return getSamplesPerPixelDescription();
    100             case TAG_PLANAR_CONFIGURATION:
    101                 return getPlanarConfigurationDescription();
    102             case TAG_YCBCR_SUBSAMPLING:
    103                 return getYCbCrSubsamplingDescription();
    104             case TAG_REFERENCE_BLACK_WHITE:
    105                 return getReferenceBlackWhiteDescription();
    10652            default:
    10753                return super.getDescription(tagType);
    108         }
    109     }
    110 
    111     @Nullable
    112     public String getReferenceBlackWhiteDescription()
    113     {
    114         int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE);
    115         if (ints == null || ints.length < 6)
    116             return null;
    117         int blackR = ints[0];
    118         int whiteR = ints[1];
    119         int blackG = ints[2];
    120         int whiteG = ints[3];
    121         int blackB = ints[4];
    122         int whiteB = ints[5];
    123         return String.format("[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB);
    124     }
    125 
    126     @Nullable
    127     public String getYCbCrSubsamplingDescription()
    128     {
    129         int[] positions = _directory.getIntArray(TAG_YCBCR_SUBSAMPLING);
    130         if (positions == null || positions.length < 2)
    131             return null;
    132         if (positions[0] == 2 && positions[1] == 1) {
    133             return "YCbCr4:2:2";
    134         } else if (positions[0] == 2 && positions[1] == 2) {
    135             return "YCbCr4:2:0";
    136         } else {
    137             return "(Unknown)";
    138         }
    139     }
    140 
    141     @Nullable
    142     public String getPlanarConfigurationDescription()
    143     {
    144         // When image format is no compression YCbCr, this value shows byte aligns of YCbCr
    145         // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling
    146         // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr
    147         // plane format.
    148         return getIndexedDescription(TAG_PLANAR_CONFIGURATION,
    149             1,
    150             "Chunky (contiguous for each subsampling pixel)",
    151             "Separate (Y-plane/Cb-plane/Cr-plane format)"
    152         );
    153     }
    154 
    155     @Nullable
    156     public String getSamplesPerPixelDescription()
    157     {
    158         String value = _directory.getString(TAG_SAMPLES_PER_PIXEL);
    159         return value == null ? null : value + " samples/pixel";
    160     }
    161 
    162     @Nullable
    163     public String getRowsPerStripDescription()
    164     {
    165         final String value = _directory.getString(TAG_ROWS_PER_STRIP);
    166         return value == null ? null : value + " rows/strip";
    167     }
    168 
    169     @Nullable
    170     public String getStripByteCountsDescription()
    171     {
    172         final String value = _directory.getString(TAG_STRIP_BYTE_COUNTS);
    173         return value == null ? null : value + " bytes";
    174     }
    175 
    176     @Nullable
    177     public String getPhotometricInterpretationDescription()
    178     {
    179         // Shows the color space of the image data components
    180         Integer value = _directory.getInteger(TAG_PHOTOMETRIC_INTERPRETATION);
    181         if (value == null)
    182             return null;
    183         switch (value) {
    184             case 0: return "WhiteIsZero";
    185             case 1: return "BlackIsZero";
    186             case 2: return "RGB";
    187             case 3: return "RGB Palette";
    188             case 4: return "Transparency Mask";
    189             case 5: return "CMYK";
    190             case 6: return "YCbCr";
    191             case 8: return "CIELab";
    192             case 9: return "ICCLab";
    193             case 10: return "ITULab";
    194             case 32803: return "Color Filter Array";
    195             case 32844: return "Pixar LogL";
    196             case 32845: return "Pixar LogLuv";
    197             case 32892: return "Linear Raw";
    198             default:
    199                 return "Unknown colour space";
    20054        }
    20155    }
     
    24195
    24296    @Nullable
    243     public String getBitsPerSampleDescription()
    244     {
    245         String value = _directory.getString(TAG_BITS_PER_SAMPLE);
    246         return value == null ? null : value + " bits/component/pixel";
    247     }
    248 
    249     @Nullable
    250     public String getThumbnailImageWidthDescription()
    251     {
    252         String value = _directory.getString(TAG_THUMBNAIL_IMAGE_WIDTH);
    253         return value == null ? null : value + " pixels";
    254     }
    255 
    256     @Nullable
    257     public String getThumbnailImageHeightDescription()
    258     {
    259         String value = _directory.getString(TAG_THUMBNAIL_IMAGE_HEIGHT);
    260         return value == null ? null : value + " pixels";
    261     }
    262 
    263     @Nullable
    26497    public String getThumbnailLengthDescription()
    26598    {
     
    274107        return value == null ? null : value + " bytes";
    275108    }
    276 
    277     @Nullable
    278     public String getYResolutionDescription()
    279     {
    280         Rational value = _directory.getRational(TAG_Y_RESOLUTION);
    281         if (value == null)
    282             return null;
    283         final String unit = getResolutionDescription();
    284         return value.toSimpleString(_allowDecimalRepresentationOfRationals) +
    285             " dots per " +
    286             (unit == null ? "unit" : unit.toLowerCase());
    287     }
    288 
    289     @Nullable
    290     public String getXResolutionDescription()
    291     {
    292         Rational value = _directory.getRational(TAG_X_RESOLUTION);
    293         if (value == null)
    294             return null;
    295         final String unit = getResolutionDescription();
    296         return value.toSimpleString(_allowDecimalRepresentationOfRationals) +
    297             " dots per " +
    298             (unit == null ? "unit" : unit.toLowerCase());
    299     }
    300 
    301     @Nullable
    302     public String getYCbCrPositioningDescription()
    303     {
    304         return getIndexedDescription(TAG_YCBCR_POSITIONING, 1, "Center of pixel array", "Datum point");
    305     }
    306 
    307     @Nullable
    308     public String getOrientationDescription()
    309     {
    310         return getIndexedDescription(TAG_ORIENTATION, 1,
    311             "Top, left side (Horizontal / normal)",
    312             "Top, right side (Mirror horizontal)",
    313             "Bottom, right side (Rotate 180)",
    314             "Bottom, left side (Mirror vertical)",
    315             "Left side, top (Mirror horizontal and rotate 270 CW)",
    316             "Right side, top (Rotate 90 CW)",
    317             "Right side, bottom (Mirror horizontal and rotate 90 CW)",
    318             "Left side, bottom (Rotate 270 CW)");
    319     }
    320 
    321     @Nullable
    322     public String getResolutionDescription()
    323     {
    324         // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
    325         return getIndexedDescription(TAG_RESOLUTION_UNIT, 1, "(No unit)", "Inch", "cm");
    326     }
    327109}
  • trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java

    r8132 r8243  
    2424import com.drew.lang.annotations.NotNull;
    2525import com.drew.lang.annotations.Nullable;
    26 import com.drew.metadata.Directory;
    2726import com.drew.metadata.MetadataException;
    2827
     
    3635 * @author Drew Noakes https://drewnoakes.com
    3736 */
    38 public class ExifThumbnailDirectory extends Directory
     37public class ExifThumbnailDirectory extends ExifDirectoryBase
    3938{
    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'.
     39    /**
     40     * The offset to thumbnail image bytes.
    4641     */
    47     public static final int TAG_BITS_PER_SAMPLE = 0x0102;
     42    public static final int TAG_THUMBNAIL_OFFSET = 0x0201;
     43    /**
     44     * The size of the thumbnail image data in bytes.
     45     */
     46    public static final int TAG_THUMBNAIL_LENGTH = 0x0202;
    4847
    4948    /**
     
    7978    public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103;
    8079
    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     /**
    101      * The position in the file of raster data.
    102      */
    103     public static final int TAG_STRIP_OFFSETS = 0x0111;
    104     public static final int TAG_ORIENTATION = 0x0112;
    105     /**
    106      * Each pixel is composed of this many samples.
    107      */
    108     public static final int TAG_SAMPLES_PER_PIXEL = 0x0115;
    109     /**
    110      * The raster is codified by a single block of data holding this many rows.
    111      */
    112     public static final int TAG_ROWS_PER_STRIP = 0x116;
    113     /**
    114      * The size of the raster data in bytes.
    115      */
    116     public static final int TAG_STRIP_BYTE_COUNTS = 0x0117;
    117     /**
    118      * When image format is no compression YCbCr, this value shows byte aligns of
    119      * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for
    120      * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and
    121      * stored to Y plane/Cb plane/Cr plane format.
    122      */
    123     public static final int TAG_X_RESOLUTION = 0x011A;
    124     public static final int TAG_Y_RESOLUTION = 0x011B;
    125     public static final int TAG_PLANAR_CONFIGURATION = 0x011C;
    126     public static final int TAG_RESOLUTION_UNIT = 0x0128;
    127     /**
    128      * The offset to thumbnail image bytes.
    129      */
    130     public static final int TAG_THUMBNAIL_OFFSET = 0x0201;
    131     /**
    132      * The size of the thumbnail image data in bytes.
    133      */
    134     public static final int TAG_THUMBNAIL_LENGTH = 0x0202;
    135     public static final int TAG_YCBCR_COEFFICIENTS = 0x0211;
    136     public static final int TAG_YCBCR_SUBSAMPLING = 0x0212;
    137     public static final int TAG_YCBCR_POSITIONING = 0x0213;
    138     public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214;
    139 
    14080    @NotNull
    14181    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
    14282
    143     static {
    144         _tagNameMap.put(TAG_THUMBNAIL_IMAGE_WIDTH, "Thumbnail Image Width");
    145         _tagNameMap.put(TAG_THUMBNAIL_IMAGE_HEIGHT, "Thumbnail Image Height");
    146         _tagNameMap.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample");
     83    static
     84    {
     85        addExifTagNames(_tagNameMap);
     86
    14787        _tagNameMap.put(TAG_THUMBNAIL_COMPRESSION, "Thumbnail Compression");
    148         _tagNameMap.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation");
    149         _tagNameMap.put(TAG_STRIP_OFFSETS, "Strip Offsets");
    150         _tagNameMap.put(TAG_ORIENTATION, "Orientation");
    151         _tagNameMap.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel");
    152         _tagNameMap.put(TAG_ROWS_PER_STRIP, "Rows Per Strip");
    153         _tagNameMap.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts");
    154         _tagNameMap.put(TAG_X_RESOLUTION, "X Resolution");
    155         _tagNameMap.put(TAG_Y_RESOLUTION, "Y Resolution");
    156         _tagNameMap.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration");
    157         _tagNameMap.put(TAG_RESOLUTION_UNIT, "Resolution Unit");
    15888        _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset");
    15989        _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length");
    160         _tagNameMap.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients");
    161         _tagNameMap.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling");
    162         _tagNameMap.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning");
    163         _tagNameMap.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White");
    16490    }
    16591
  • trunk/src/com/drew/metadata/exif/ExifTiffHandler.java

    r8132 r8243  
    5757        final int standardTiffMarker = 0x002A;
    5858        final int olympusRawTiffMarker = 0x4F52; // for ORF files
     59        final int olympusRawTiffMarker2 = 0x5352; // for ORF files
    5960        final int panasonicRawTiffMarker = 0x0055; // for RW2 files
    6061
    61         if (marker != standardTiffMarker && marker != olympusRawTiffMarker && marker != panasonicRawTiffMarker) {
     62        if (marker != standardTiffMarker && marker != olympusRawTiffMarker && marker != olympusRawTiffMarker2 && marker != panasonicRawTiffMarker) {
    6263            throw new TiffProcessingException("Unexpected TIFF marker: 0x" + Integer.toHexString(marker));
    6364        }
     
    127128        if (_storeThumbnailBytes) {
    128129            // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
    129             ExifThumbnailDirectory thumbnailDirectory = _metadata.getDirectory(ExifThumbnailDirectory.class);
     130            ExifThumbnailDirectory thumbnailDirectory = _metadata.getFirstDirectoryOfType(ExifThumbnailDirectory.class);
    130131            if (thumbnailDirectory != null && thumbnailDirectory.containsTag(ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION)) {
    131132                Integer offset = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET);
     
    149150    {
    150151        // Determine the camera model and makernote format.
    151         Directory ifd0Directory = _metadata.getDirectory(ExifIFD0Directory.class);
     152        Directory ifd0Directory = _metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    152153
    153154        if (ifd0Directory == null)
     
    219220        } else if ("KDK".equals(firstThreeChars)) {
    220221            reader.setMotorolaByteOrder(firstSevenChars.equals("KDK INFO"));
    221             processKodakMakernote(_metadata.getOrCreateDirectory(KodakMakernoteDirectory.class), makernoteOffset, reader);
     222            KodakMakernoteDirectory directory = new KodakMakernoteDirectory();
     223            _metadata.addDirectory(directory);
     224            processKodakMakernote(directory, makernoteOffset, reader);
    222225        } else if ("Canon".equalsIgnoreCase(cameraMake)) {
    223226            pushDirectory(CanonMakernoteDirectory.class);
  • trunk/src/com/drew/metadata/exif/GpsDescriptor.java

    r8132 r8243  
    109109    {
    110110        // time in hour, min, sec
    111         int[] timeComponents = _directory.getIntArray(TAG_TIME_STAMP);
    112         return timeComponents == null ? null : String.format("%d:%d:%d UTC", timeComponents[0], timeComponents[1], timeComponents[2]);
     111        Rational[] timeComponents = _directory.getRationalArray(TAG_TIME_STAMP);
     112        DecimalFormat df = new DecimalFormat("00.00");
     113        return timeComponents == null
     114            ? null
     115            : String.format("%02d:%02d:%s UTC",
     116                timeComponents[0].intValue(),
     117                timeComponents[1].intValue(),
     118                df.format(timeComponents[2].doubleValue()));
    113119    }
    114120
  • trunk/src/com/drew/metadata/exif/GpsDirectory.java

    r8132 r8243  
    2525import com.drew.lang.annotations.NotNull;
    2626import com.drew.lang.annotations.Nullable;
    27 import com.drew.metadata.Directory;
    2827
    2928import java.util.HashMap;
     
    3433 * @author Drew Noakes https://drewnoakes.com
    3534 */
    36 public class GpsDirectory extends Directory
     35public class GpsDirectory extends ExifDirectoryBase
    3736{
    3837    /** GPS tag version GPSVersionID 0 0 BYTE 4 */
     
    102101    static
    103102    {
     103        addExifTagNames(_tagNameMap);
     104
    104105        _tagNameMap.put(TAG_VERSION_ID, "GPS Version ID");
    105106        _tagNameMap.put(TAG_LATITUDE_REF, "GPS Latitude Ref");
  • trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java

    r8132 r8243  
    8282            case CameraSettings.TAG_EXPOSURE_MODE:
    8383                return getExposureModeDescription();
     84            case CameraSettings.TAG_LENS_TYPE:
     85                return getLensTypeDescription();
    8486            case CameraSettings.TAG_LONG_FOCAL_LENGTH:
    8587                return getLongFocalLengthDescription();
     
    451453
    452454    @Nullable
     455    public String getLensTypeDescription() {
     456        Integer value = _directory.getInteger(CameraSettings.TAG_LENS_TYPE);
     457        if (value == null)
     458            return null;
     459
     460        return "Lens type: " + Integer.toString(value);
     461    }
     462
     463    @Nullable
    453464    public String getAfPointSelectedDescription()
    454465    {
  • trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDirectory.java

    r8132 r8243  
    245245        public static final int TAG_EXPOSURE_MODE = OFFSET + 0x14;
    246246        public static final int TAG_UNKNOWN_7 = OFFSET + 0x15;
    247         public static final int TAG_UNKNOWN_8 = OFFSET + 0x16;
     247        public static final int TAG_LENS_TYPE = OFFSET + 0x16;
    248248        public static final int TAG_LONG_FOCAL_LENGTH = OFFSET + 0x17;
    249249        public static final int TAG_SHORT_FOCAL_LENGTH = OFFSET + 0x18;
     
    519519        _tagNameMap.put(CameraSettings.TAG_FOCUS_TYPE, "Focus Type");
    520520        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_7, "Unknown Camera Setting 7");
    521         _tagNameMap.put(CameraSettings.TAG_UNKNOWN_8, "Unknown Camera Setting 8");
     521        _tagNameMap.put(CameraSettings.TAG_LENS_TYPE, "Lens Type");
    522522        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_9, "Unknown Camera Setting 9");
    523523        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_10, "Unknown Camera Setting 10");
  • trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDirectory.java

    r8132 r8243  
    5151    public static final int TAG_MINOLTA_THUMBNAIL_LENGTH = 0x0089;
    5252
     53    public static final int TAG_THUMBNAIL_IMAGE = 0x0100;
     54
    5355    /**
    5456     * Used by Konica / Minolta cameras
     
    8385    public static final int TAG_IMAGE_QUALITY_2 = 0x0103;
    8486
     87    public static final int TAG_BODY_FIRMWARE_VERSION = 0x0104;
    8588
    8689    /**
     
    136139    public static final int TAG_ORIGINAL_MANUFACTURER_MODEL = 0x020D;
    137140
     141    public static final int TAG_PREVIEW_IMAGE = 0x0280;
     142    public static final int TAG_PRE_CAPTURE_FRAMES = 0x0300;
     143    public static final int TAG_WHITE_BOARD = 0x0301;
     144    public static final int TAG_ONE_TOUCH_WB = 0x0302;
     145    public static final int TAG_WHITE_BALANCE_BRACKET = 0x0303;
     146    public static final int TAG_WHITE_BALANCE_BIAS = 0x0304;
     147    public static final int TAG_SCENE_MODE = 0x0403;
     148    public static final int TAG_FIRMWARE = 0x0404;
     149
    138150    /**
    139151     * See the PIM specification here:
     
    142154    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
    143155
    144     public static final int TAG_DATA_DUMP = 0x0F00;
     156    public static final int TAG_DATA_DUMP_1 = 0x0F00;
     157    public static final int TAG_DATA_DUMP_2 = 0x0F01;
    145158
    146159    public static final int TAG_SHUTTER_SPEED_VALUE = 0x1000;
     
    149162    public static final int TAG_BRIGHTNESS_VALUE = 0x1003;
    150163    public static final int TAG_FLASH_MODE = 0x1004;
     164    public static final int TAG_FLASH_DEVICE = 0x1005;
    151165    public static final int TAG_BRACKET = 0x1006;
     166    public static final int TAG_SENSOR_TEMPERATURE = 0x1007;
     167    public static final int TAG_LENS_TEMPERATURE = 0x1008;
     168    public static final int TAG_LIGHT_CONDITION = 0x1009;
    152169    public static final int TAG_FOCUS_RANGE = 0x100A;
    153170    public static final int TAG_FOCUS_MODE = 0x100B;
     
    156173    public static final int TAG_MACRO_FOCUS = 0x100E;
    157174    public static final int TAG_SHARPNESS = 0x100F;
     175    public static final int TAG_FLASH_CHARGE_LEVEL = 0x1010;
    158176    public static final int TAG_COLOUR_MATRIX = 0x1011;
    159177    public static final int TAG_BLACK_LEVEL = 0x1012;
     178//    public static final int TAG_ = 0x1013;
     179//    public static final int TAG_ = 0x1014;
    160180    public static final int TAG_WHITE_BALANCE = 0x1015;
     181//    public static final int TAG_ = 0x1016;
    161182    public static final int TAG_RED_BIAS = 0x1017;
    162183    public static final int TAG_BLUE_BIAS = 0x1018;
     184    public static final int TAG_COLOR_MATRIX_NUMBER = 0x1019;
    163185    public static final int TAG_SERIAL_NUMBER = 0x101A;
     186//    public static final int TAG_ = 0x101B;
     187//    public static final int TAG_ = 0x101C;
     188//    public static final int TAG_ = 0x101D;
     189//    public static final int TAG_ = 0x101E;
     190//    public static final int TAG_ = 0x101F;
     191//    public static final int TAG_ = 0x1020;
     192//    public static final int TAG_ = 0x1021;
     193//    public static final int TAG_ = 0x1022;
    164194    public static final int TAG_FLASH_BIAS = 0x1023;
     195//    public static final int TAG_ = 0x1024;
     196//    public static final int TAG_ = 0x1025;
     197    public static final int TAG_EXTERNAL_FLASH_BOUNCE = 0x1026;
     198    public static final int TAG_EXTERNAL_FLASH_ZOOM = 0x1027;
     199    public static final int TAG_EXTERNAL_FLASH_MODE = 0x1028;
    165200    public static final int TAG_CONTRAST = 0x1029;
    166201    public static final int TAG_SHARPNESS_FACTOR = 0x102A;
     
    170205    public static final int TAG_FINAL_WIDTH = 0x102E;
    171206    public static final int TAG_FINAL_HEIGHT = 0x102F;
     207//    public static final int TAG_ = 0x1030;
     208//    public static final int TAG_ = 0x1031;
     209//    public static final int TAG_ = 0x1032;
     210//    public static final int TAG_ = 0x1033;
    172211    public static final int TAG_COMPRESSION_RATIO = 0x1034;
     212    public static final int TAG_THUMBNAIL = 0x1035;
     213    public static final int TAG_THUMBNAIL_OFFSET = 0x1036;
     214    public static final int TAG_THUMBNAIL_LENGTH = 0x1037;
     215//    public static final int TAG_ = 0x1038;
     216    public static final int TAG_CCD_SCAN_MODE = 0x1039;
     217    public static final int TAG_NOISE_REDUCTION = 0x103A;
     218    public static final int TAG_INFINITY_LENS_STEP = 0x103B;
     219    public static final int TAG_NEAR_LENS_STEP = 0x103C;
     220    public static final int TAG_EQUIPMENT = 0x2010;
     221    public static final int TAG_CAMERA_SETTINGS = 0x2020;
     222    public static final int TAG_RAW_DEVELOPMENT = 0x2030;
     223    public static final int TAG_RAW_DEVELOPMENT_2 = 0x2031;
     224    public static final int TAG_IMAGE_PROCESSING = 0x2040;
     225    public static final int TAG_FOCUS_INFO = 0x2050;
     226    public static final int TAG_RAW_INFO = 0x3000;
    173227
    174228    public final static class CameraSettings
     
    191245        public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 14;
    192246        public static final int TAG_BRACKET_STEP = OFFSET + 15;
    193 
     247        // 16 missing
    194248        public static final int TAG_INTERVAL_LENGTH = OFFSET + 17;
    195249        public static final int TAG_INTERVAL_NUMBER = OFFSET + 18;
     
    200254        public static final int TAG_TIME = OFFSET + 23;
    201255        public static final int TAG_MAX_APERTURE_AT_FOCAL_LENGTH = OFFSET + 24;
    202 
     256        // 25, 26 missing
    203257        public static final int TAG_FILE_NUMBER_MEMORY = OFFSET + 27;
    204258        public static final int TAG_LAST_FILE_NUMBER = OFFSET + 28;
     
    232286
    233287    static {
     288        _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
     289        _tagNameMap.put(TAG_CAMERA_SETTINGS_1, "Camera Settings");
     290        _tagNameMap.put(TAG_CAMERA_SETTINGS_2, "Camera Settings");
     291        _tagNameMap.put(TAG_COMPRESSED_IMAGE_SIZE, "Compressed Image Size");
     292        _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_OFFSET_1, "Thumbnail Offset");
     293        _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_OFFSET_2, "Thumbnail Offset");
     294        _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_LENGTH, "Thumbnail Length");
     295        _tagNameMap.put(TAG_THUMBNAIL_IMAGE, "Thumbnail Image");
     296        _tagNameMap.put(TAG_COLOUR_MODE, "Colour Mode");
     297        _tagNameMap.put(TAG_IMAGE_QUALITY_1, "Image Quality");
     298        _tagNameMap.put(TAG_IMAGE_QUALITY_2, "Image Quality");
     299        _tagNameMap.put(TAG_BODY_FIRMWARE_VERSION, "Body Firmware Version");
    234300        _tagNameMap.put(TAG_SPECIAL_MODE, "Special Mode");
    235301        _tagNameMap.put(TAG_JPEG_QUALITY, "JPEG Quality");
     
    242308        _tagNameMap.put(TAG_PICT_INFO, "Pict Info");
    243309        _tagNameMap.put(TAG_CAMERA_ID, "Camera Id");
    244         _tagNameMap.put(TAG_DATA_DUMP, "Data Dump");
    245         _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
    246         _tagNameMap.put(TAG_CAMERA_SETTINGS_1, "Camera Settings");
    247         _tagNameMap.put(TAG_CAMERA_SETTINGS_2, "Camera Settings");
    248         _tagNameMap.put(TAG_COMPRESSED_IMAGE_SIZE, "Compressed Image Size");
    249         _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_OFFSET_1, "Thumbnail Offset");
    250         _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_OFFSET_2, "Thumbnail Offset");
    251         _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_LENGTH, "Thumbnail Length");
    252         _tagNameMap.put(TAG_COLOUR_MODE, "Colour Mode");
    253         _tagNameMap.put(TAG_IMAGE_QUALITY_1, "Image Quality");
    254         _tagNameMap.put(TAG_IMAGE_QUALITY_2, "Image Quality");
     310        _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
    255311        _tagNameMap.put(TAG_IMAGE_HEIGHT, "Image Height");
    256         _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
    257312        _tagNameMap.put(TAG_ORIGINAL_MANUFACTURER_MODEL, "Original Manufacturer Model");
     313        _tagNameMap.put(TAG_PREVIEW_IMAGE, "Preview Image");
     314        _tagNameMap.put(TAG_PRE_CAPTURE_FRAMES, "Pre Capture Frames");
     315        _tagNameMap.put(TAG_WHITE_BOARD, "White Board");
     316        _tagNameMap.put(TAG_ONE_TOUCH_WB, "One Touch WB");
     317        _tagNameMap.put(TAG_WHITE_BALANCE_BRACKET, "White Balance Bracket");
     318        _tagNameMap.put(TAG_WHITE_BALANCE_BIAS, "White Balance Bias");
     319        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
     320        _tagNameMap.put(TAG_FIRMWARE, "Firmware");
    258321        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
    259 
     322        _tagNameMap.put(TAG_DATA_DUMP_1, "Data Dump");
     323        _tagNameMap.put(TAG_DATA_DUMP_2, "Data Dump 2");
    260324        _tagNameMap.put(TAG_SHUTTER_SPEED_VALUE, "Shutter Speed Value");
    261325        _tagNameMap.put(TAG_ISO_VALUE, "ISO Value");
     
    263327        _tagNameMap.put(TAG_BRIGHTNESS_VALUE, "Brightness Value");
    264328        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
     329        _tagNameMap.put(TAG_FLASH_DEVICE, "Flash Device");
    265330        _tagNameMap.put(TAG_BRACKET, "Bracket");
     331        _tagNameMap.put(TAG_SENSOR_TEMPERATURE, "Sensor Temperature");
     332        _tagNameMap.put(TAG_LENS_TEMPERATURE, "Lens Temperature");
     333        _tagNameMap.put(TAG_LIGHT_CONDITION, "Light Condition");
    266334        _tagNameMap.put(TAG_FOCUS_RANGE, "Focus Range");
    267335        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
     
    270338        _tagNameMap.put(TAG_MACRO_FOCUS, "Macro Focus");
    271339        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
     340        _tagNameMap.put(TAG_FLASH_CHARGE_LEVEL, "Flash Charge Level");
    272341        _tagNameMap.put(TAG_COLOUR_MATRIX, "Colour Matrix");
    273342        _tagNameMap.put(TAG_BLACK_LEVEL, "Black Level");
     
    275344        _tagNameMap.put(TAG_RED_BIAS, "Red Bias");
    276345        _tagNameMap.put(TAG_BLUE_BIAS, "Blue Bias");
     346        _tagNameMap.put(TAG_COLOR_MATRIX_NUMBER, "Color Matrix Number");
    277347        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
    278348        _tagNameMap.put(TAG_FLASH_BIAS, "Flash Bias");
     349        _tagNameMap.put(TAG_EXTERNAL_FLASH_BOUNCE, "External Flash Bounce");
     350        _tagNameMap.put(TAG_EXTERNAL_FLASH_ZOOM, "External Flash Zoom");
     351        _tagNameMap.put(TAG_EXTERNAL_FLASH_MODE, "External Flash Mode");
    279352        _tagNameMap.put(TAG_CONTRAST, "Contrast");
    280353        _tagNameMap.put(TAG_SHARPNESS_FACTOR, "Sharpness Factor");
     
    285358        _tagNameMap.put(TAG_FINAL_HEIGHT, "Final Height");
    286359        _tagNameMap.put(TAG_COMPRESSION_RATIO, "Compression Ratio");
     360        _tagNameMap.put(TAG_THUMBNAIL, "Thumbnail");
     361        _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset");
     362        _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length");
     363        _tagNameMap.put(TAG_CCD_SCAN_MODE, "CCD Scan Mode");
     364        _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction");
     365        _tagNameMap.put(TAG_INFINITY_LENS_STEP, "Infinity Lens Step");
     366        _tagNameMap.put(TAG_NEAR_LENS_STEP, "Near Lens Step");
     367        _tagNameMap.put(TAG_EQUIPMENT, "Equipment");
     368        _tagNameMap.put(TAG_CAMERA_SETTINGS, "Camera Settings");
     369        _tagNameMap.put(TAG_RAW_DEVELOPMENT, "Raw Development");
     370        _tagNameMap.put(TAG_RAW_DEVELOPMENT_2, "Raw Development 2");
     371        _tagNameMap.put(TAG_IMAGE_PROCESSING, "Image Processing");
     372        _tagNameMap.put(TAG_FOCUS_INFO, "Focus Info");
     373        _tagNameMap.put(TAG_RAW_INFO, "Raw Info");
    287374
    288375        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode");
  • trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDirectory.java

    r8132 r8243  
    3535 * Describes tags specific to Panasonic and Leica cameras.
    3636 *
    37  * @author Drew Noakes https://drewnoakes.com, Philipp Sandhaus
     37 * @author Drew Noakes https://drewnoakes.com
     38 * @author Philipp Sandhaus
    3839 */
    3940public class PanasonicMakernoteDirectory extends Directory
  • trunk/src/com/drew/metadata/iptc/IptcDescriptor.java

    r8132 r8243  
    4949            case IptcDirectory.TAG_KEYWORDS:
    5050                return getKeywordsDescription();
     51            case IptcDirectory.TAG_TIME_CREATED:
     52                return getTimeCreatedDescription();
     53            case IptcDirectory.TAG_DIGITAL_TIME_CREATED:
     54                return getDigitalTimeCreatedDescription();
    5155            default:
    5256                return super.getDescription(tagType);
     
    227231    public String getTimeCreatedDescription()
    228232    {
    229         return _directory.getString(IptcDirectory.TAG_TIME_CREATED);
     233        String s = _directory.getString(IptcDirectory.TAG_TIME_CREATED);
     234        if (s == null)
     235            return null;
     236        if (s.length() == 6 || s.length() == 11)
     237            return s.substring(0, 2) + ':' + s.substring(2, 4) + ':' + s.substring(4);
     238        return s;
     239    }
     240
     241    @Nullable
     242    public String getDigitalTimeCreatedDescription()
     243    {
     244        String s = _directory.getString(IptcDirectory.TAG_DIGITAL_TIME_CREATED);
     245        if (s == null)
     246            return null;
     247        if (s.length() == 6 || s.length() == 11)
     248            return s.substring(0, 2) + ':' + s.substring(2, 4) + ':' + s.substring(4);
     249        return s;
    230250    }
    231251
  • trunk/src/com/drew/metadata/iptc/IptcReader.java

    r8132 r8243  
    6363    }
    6464
    65     public boolean canProcess(@NotNull byte[] segmentBytes, @NotNull JpegSegmentType segmentType)
    66     {
    67         // Check whether the first byte resembles
    68         return segmentBytes.length != 0 && segmentBytes[0] == 0x1c;
    69     }
    70 
    71     public void extract(@NotNull byte[] segmentBytes, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
    72     {
    73         extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.length);
     65    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
     66    {
     67        for (byte[] segmentBytes : segments) {
     68            // Ensure data starts with the IPTC marker byte
     69            if (segmentBytes.length != 0 && segmentBytes[0] == 0x1c) {
     70                extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.length);
     71            }
     72        }
    7473    }
    7574
     
    7978    public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length)
    8079    {
    81         IptcDirectory directory = metadata.getOrCreateDirectory(IptcDirectory.class);
     80        IptcDirectory directory = new IptcDirectory();
     81        metadata.addDirectory(directory);
    8282
    8383        int offset = 0;
  • trunk/src/com/drew/metadata/jpeg/JpegCommentReader.java

    r8132 r8243  
    4848    }
    4949
    50     public void extract(@NotNull byte[] segmentBytes, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
     50    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
    5151    {
    52         JpegCommentDirectory directory = metadata.getOrCreateDirectory(JpegCommentDirectory.class);
     52        for (byte[] segmentBytes : segments) {
     53            JpegCommentDirectory directory = new JpegCommentDirectory();
     54            metadata.addDirectory(directory);
    5355
    54         // The entire contents of the directory are the comment
    55         directory.setString(JpegCommentDirectory.TAG_COMMENT, new String(segmentBytes));
     56            // The entire contents of the directory are the comment
     57            directory.setString(JpegCommentDirectory.TAG_COMMENT, new String(segmentBytes));
     58        }
    5659    }
    5760}
  • trunk/src/com/drew/metadata/jpeg/JpegReader.java

    r8132 r8243  
    6363    }
    6464
    65     public boolean canProcess(@NotNull byte[] segmentBytes, @NotNull JpegSegmentType segmentType)
     65    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
    6666    {
    67         return true;
     67        for (byte[] segmentBytes : segments) {
     68            extract(segmentBytes, metadata, segmentType);
     69        }
    6870    }
    6971
    70     public void extract(@NotNull byte[] segmentBytes, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
     72    public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType)
    7173    {
    72         if (metadata.containsDirectory(JpegDirectory.class)) {
    73             // If this directory is already present, discontinue this operation.
    74             // We only store metadata for the *first* matching SOFn segment.
    75             return;
    76         }
    77 
    78         JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class);
     74        JpegDirectory directory = new JpegDirectory();
     75        metadata.addDirectory(directory);
    7976
    8077        // The value of TAG_COMPRESSION_TYPE is determined by the segment type found
     
    10198                directory.setObject(JpegDirectory.TAG_COMPONENT_DATA_1 + i, component);
    10299            }
    103 
    104100        } catch (IOException ex) {
    105101            directory.addError(ex.getMessage());
  • trunk/src/com/drew/metadata/tiff/DirectoryTiffHandler.java

    r8132 r8243  
    4141    protected final Metadata _metadata;
    4242
    43     protected DirectoryTiffHandler(Metadata metadata, Class<? extends Directory> initialDirectory)
     43    protected DirectoryTiffHandler(Metadata metadata, Class<? extends Directory> initialDirectoryClass)
    4444    {
    4545        _metadata = metadata;
    46         _currentDirectory = _metadata.getOrCreateDirectory(initialDirectory);
     46        try {
     47            _currentDirectory = initialDirectoryClass.newInstance();
     48        } catch (InstantiationException e) {
     49            throw new RuntimeException(e);
     50        } catch (IllegalAccessException e) {
     51            throw new RuntimeException(e);
     52        }
     53        _metadata.addDirectory(_currentDirectory);
    4754    }
    4855
     
    5461    protected void pushDirectory(@NotNull Class<? extends Directory> directoryClass)
    5562    {
    56         assert(directoryClass != _currentDirectory.getClass());
    5763        _directoryStack.push(_currentDirectory);
    58         _currentDirectory = _metadata.getOrCreateDirectory(directoryClass);
     64        try {
     65            _currentDirectory = directoryClass.newInstance();
     66        } catch (InstantiationException e) {
     67            throw new RuntimeException(e);
     68        } catch (IllegalAccessException e) {
     69            throw new RuntimeException(e);
     70        }
     71        _metadata.addDirectory(_currentDirectory);
    5972    }
    6073
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    r8132 r8243  
    603603        try {
    604604            metadata = JpegMetadataReader.readMetadata(e.getFile());
    605             dirExif = metadata.getDirectory(ExifIFD0Directory.class);
    606             dirGps = metadata.getDirectory(GpsDirectory.class);
     605            dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
     606            dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    607607        } catch (CompoundException | IOException p) {
    608608            e.setExifCoor(null);
  • trunk/src/org/openstreetmap/josm/tools/ExifReader.java

    r8132 r8243  
    8787        try {
    8888            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
    89             final Directory dir = metadata.getDirectory(ExifIFD0Directory.class);
     89            final Directory dir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    9090            return dir.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    9191        } catch (JpegProcessingException | MetadataException | IOException e) {
     
    104104        try {
    105105            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
    106             final GpsDirectory dirGps = metadata.getDirectory(GpsDirectory.class);
     106            final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    107107            return readLatLon(dirGps);
    108108        } catch (JpegProcessingException e) {
     
    141141        try {
    142142            final Metadata metadata = JpegMetadataReader.readMetadata(filename);
    143             final GpsDirectory dirGps = metadata.getDirectory(GpsDirectory.class);
     143            final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    144144            return readDirection(dirGps);
    145145        } catch (JpegProcessingException e) {
Note: See TracChangeset for help on using the changeset viewer.