Index: /trunk/build.xml
===================================================================
--- /trunk/build.xml	(revision 16024)
+++ /trunk/build.xml	(revision 16025)
@@ -294,5 +294,5 @@
     <target name="compile-cots" depends="init">
         <!-- COTS -->
-        <javac srcdir="${src.dir}" includes="com/**,oauth/**,org/apache/commons/**" nowarn="on" encoding="iso-8859-1"
+        <javac srcdir="${src.dir}" includes="com/google/**,com/kitfox/**,oauth/**,org/apache/commons/**" nowarn="on" encoding="iso-8859-1"
             destdir="${build.dir}" target="${java.lang.version}" source="${java.lang.version}" debug="on" includeAntRuntime="false" createMissingPackageInfoClass="false">
             <!-- get rid of "internal proprietary API" warning -->
@@ -1112,4 +1112,34 @@
                 <exclude name="META-INF/**"/>
                 <exclude name="*"/>
+                <exclude name="com/drew/imaging/avi/**"/>
+                <exclude name="com/drew/imaging/bmp/**"/>
+                <exclude name="com/drew/imaging/eps/**"/>
+                <exclude name="com/drew/imaging/gif/**"/>
+                <exclude name="com/drew/imaging/heif/**"/>
+                <exclude name="com/drew/imaging/ico/**"/>
+                <exclude name="com/drew/imaging/mp3/**"/>
+                <exclude name="com/drew/imaging/mp4/**"/>
+                <exclude name="com/drew/imaging/pcx/**"/>
+                <exclude name="com/drew/imaging/png/**"/>
+                <exclude name="com/drew/imaging/psd/**"/>
+                <exclude name="com/drew/imaging/quicktime/**"/>
+                <exclude name="com/drew/imaging/raf/**"/>
+                <exclude name="com/drew/imaging/riff/**"/>
+                <exclude name="com/drew/imaging/wav/**"/>
+                <exclude name="com/drew/imaging/webp/**"/>
+                <exclude name="com/drew/metadata/avi/**"/>
+                <exclude name="com/drew/metadata/bmp/**"/>
+                <exclude name="com/drew/metadata/eps/**"/>
+                <exclude name="com/drew/metadata/gif/**"/>
+                <exclude name="com/drew/metadata/heif/**"/>
+                <exclude name="com/drew/metadata/ico/**"/>
+                <exclude name="com/drew/metadata/mov/**"/>
+                <exclude name="com/drew/metadata/mp3/**"/>
+                <exclude name="com/drew/metadata/mp4/**"/>
+                <exclude name="com/drew/metadata/pcx/**"/>
+                <exclude name="com/drew/metadata/png/**"/>
+                <exclude name="com/drew/metadata/wav/**"/>
+                <exclude name="com/drew/metadata/webp/**"/>
+                <exclude name="com/drew/tools/**"/>
                 <exclude name="org/apache/commons/compress/PasswordRequiredException.class"/>
                 <exclude name="org/apache/commons/compress/archivers/**"/>
Index: /trunk/ivy.xml
===================================================================
--- /trunk/ivy.xml	(revision 16024)
+++ /trunk/ivy.xml	(revision 16025)
@@ -21,4 +21,5 @@
         <dependency org="org.apache.commons" name="commons-compress" rev="1.20" conf="api->default"/>
         <dependency org="org.tukaani" name="xz" rev="1.8" conf="api->default"/>
+        <dependency org="com.drewnoakes" name="metadata-extractor" rev="2.13.0" conf="api->default"/>
         <dependency org="ch.poole" name="OpeningHoursParser" rev="0.21.0" conf="api->default"/>
     </dependencies>
Index: unk/src/com/drew/imaging/ImageProcessingException.java
===================================================================
--- /trunk/src/com/drew/imaging/ImageProcessingException.java	(revision 16024)
+++ 	(revision )
@@ -1,49 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging;
-
-import com.drew.lang.CompoundException;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * An exception class thrown upon an unexpected condition that was fatal for the processing of an image.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class ImageProcessingException extends CompoundException
-{
-    private static final long serialVersionUID = -9115669182209912676L;
-
-    public ImageProcessingException(@Nullable String message)
-    {
-        super(message);
-    }
-
-    public ImageProcessingException(@Nullable String message, @Nullable Throwable cause)
-    {
-        super(message, cause);
-    }
-
-    public ImageProcessingException(@Nullable Throwable cause)
-    {
-        super(cause);
-    }
-}
Index: unk/src/com/drew/imaging/PhotographicConversions.java
===================================================================
--- /trunk/src/com/drew/imaging/PhotographicConversions.java	(revision 16024)
+++ 	(revision )
@@ -1,61 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging;
-
-/**
- * Contains helper methods that perform photographic conversions.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public final class PhotographicConversions
-{
-    public final static double ROOT_TWO = Math.sqrt(2);
-
-    private PhotographicConversions() throws Exception
-    {
-        throw new Exception("Not intended for instantiation.");
-    }
-
-    /**
-     * Converts an aperture value to its corresponding F-stop number.
-     *
-     * @param aperture the aperture value to convert
-     * @return the F-stop number of the specified aperture
-     */
-    public static double apertureToFStop(double aperture)
-    {
-        return Math.pow(ROOT_TWO, aperture);
-
-        // NOTE jhead uses a different calculation as far as i can tell...  this confuses me...
-        // fStop = (float)Math.exp(aperture * Math.log(2) * 0.5));
-    }
-
-    /**
-     * Converts a shutter speed to an exposure time.
-     *
-     * @param shutterSpeed the shutter speed to convert
-     * @return the exposure time of the specified shutter speed
-     */
-    public static double shutterSpeedToExposureTime(double shutterSpeed)
-    {
-        return (float) (1 / Math.exp(shutterSpeed * Math.log(2)));
-    }
-}
Index: unk/src/com/drew/imaging/jpeg/JpegMetadataReader.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java	(revision 16024)
+++ 	(revision )
@@ -1,143 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.jpeg;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.drew.lang.StreamReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Metadata;
-//import com.drew.metadata.adobe.AdobeJpegReader;
-import com.drew.metadata.exif.ExifReader;
-import com.drew.metadata.file.FileSystemMetadataReader;
-import com.drew.metadata.icc.IccReader;
-import com.drew.metadata.iptc.IptcReader;
-//import com.drew.metadata.jfif.JfifReader;
-//import com.drew.metadata.jfxx.JfxxReader;
-import com.drew.metadata.jpeg.JpegCommentReader;
-import com.drew.metadata.jpeg.JpegDhtReader;
-import com.drew.metadata.jpeg.JpegDnlReader;
-import com.drew.metadata.jpeg.JpegReader;
-import com.drew.metadata.photoshop.DuckyReader;
-import com.drew.metadata.photoshop.PhotoshopReader;
-//import com.drew.metadata.xmp.XmpReader;
-
-/**
- * Obtains all available metadata from JPEG formatted files.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class JpegMetadataReader
-{
-    public static final Iterable<JpegSegmentMetadataReader> ALL_READERS = Arrays.asList(
-            new JpegReader(),
-            new JpegCommentReader(),
-            //new JfifReader(),
-            //new JfxxReader(),
-            new ExifReader(),
-            //new XmpReader(),
-            new IccReader(),
-            new PhotoshopReader(),
-            new DuckyReader(),
-            new IptcReader(),
-            //new AdobeJpegReader(),
-            new JpegDhtReader(),
-            new JpegDnlReader()
-    );
-
-    @NotNull
-    public static Metadata readMetadata(@NotNull InputStream inputStream, @Nullable Iterable<JpegSegmentMetadataReader> readers) throws JpegProcessingException, IOException
-    {
-        Metadata metadata = new Metadata();
-        process(metadata, inputStream, readers);
-        return metadata;
-    }
-
-    @NotNull
-    public static Metadata readMetadata(@NotNull InputStream inputStream) throws JpegProcessingException, IOException
-    {
-        return readMetadata(inputStream, null);
-    }
-
-    @NotNull
-    public static Metadata readMetadata(@NotNull File file, @Nullable Iterable<JpegSegmentMetadataReader> readers) throws JpegProcessingException, IOException
-    {
-        InputStream inputStream = new FileInputStream(file);
-        Metadata metadata;
-        try {
-            metadata = readMetadata(inputStream, readers);
-        } finally {
-            inputStream.close();
-        }
-        new FileSystemMetadataReader().read(file, metadata);
-        return metadata;
-    }
-
-    @NotNull
-    public static Metadata readMetadata(@NotNull File file) throws JpegProcessingException, IOException
-    {
-        return readMetadata(file, null);
-    }
-
-    public static void process(@NotNull Metadata metadata, @NotNull InputStream inputStream) throws JpegProcessingException, IOException
-    {
-        process(metadata, inputStream, null);
-    }
-
-    public static void process(@NotNull Metadata metadata, @NotNull InputStream inputStream, @Nullable Iterable<JpegSegmentMetadataReader> readers) throws JpegProcessingException, IOException
-    {
-        if (readers == null)
-            readers = ALL_READERS;
-
-        Set<JpegSegmentType> segmentTypes = new HashSet<JpegSegmentType>();
-        for (JpegSegmentMetadataReader reader : readers) {
-            for (JpegSegmentType type : reader.getSegmentTypes()) {
-                segmentTypes.add(type);
-            }
-        }
-
-        JpegSegmentData segmentData = JpegSegmentReader.readSegments(new StreamReader(inputStream), segmentTypes);
-
-        processJpegSegmentData(metadata, readers, segmentData);
-    }
-
-    public static void processJpegSegmentData(Metadata metadata, Iterable<JpegSegmentMetadataReader> readers, JpegSegmentData segmentData)
-    {
-        // Pass the appropriate byte arrays to each reader.
-        for (JpegSegmentMetadataReader reader : readers) {
-            for (JpegSegmentType segmentType : reader.getSegmentTypes()) {
-                reader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType);
-            }
-        }
-    }
-
-    private JpegMetadataReader() throws Exception
-    {
-        throw new Exception("Not intended for instantiation");
-    }
-}
Index: unk/src/com/drew/imaging/jpeg/JpegProcessingException.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java	(revision 16024)
+++ 	(revision )
@@ -1,49 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.jpeg;
-
-import com.drew.imaging.ImageProcessingException;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * An exception class thrown upon unexpected and fatal conditions while processing a JPEG file.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class JpegProcessingException extends ImageProcessingException
-{
-    private static final long serialVersionUID = -7870179776125450158L;
-
-    public JpegProcessingException(@Nullable String message)
-    {
-        super(message);
-    }
-
-    public JpegProcessingException(@Nullable String message, @Nullable Throwable cause)
-    {
-        super(message, cause);
-    }
-
-    public JpegProcessingException(@Nullable Throwable cause)
-    {
-        super(cause);
-    }
-}
Index: unk/src/com/drew/imaging/jpeg/JpegSegmentData.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java	(revision 16024)
+++ 	(revision )
@@ -1,270 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.util.*;
-
-/**
- * Holds a collection of JPEG data segments.  This need not necessarily be all segments
- * within the JPEG. For example, it may be convenient to store only the non-image
- * segments when analysing metadata.
- * <p>
- * Segments are keyed via their {@link JpegSegmentType}. Where multiple segments use the
- * same segment type, they will all be stored and available.
- * <p>
- * Each segment type may contain multiple entries. Conceptually the model is:
- * <code>Map&lt;JpegSegmentType, Collection&lt;byte[]&gt;&gt;</code>. This class provides
- * convenience methods around that structure.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class JpegSegmentData
-{
-    // TODO key this on JpegSegmentType rather than Byte, and hopefully lose much of the use of 'byte' with this class
-    @NotNull
-    private final HashMap<Byte, List<byte[]>> _segmentDataMap = new HashMap<Byte, List<byte[]>>(10);
-
-    /**
-     * Adds segment bytes to the collection.
-     *
-     * @param segmentType  the type of the segment being added
-     * @param segmentBytes the byte array holding data for the segment being added
-     */
-    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
-    public void addSegment(byte segmentType, @NotNull byte[] segmentBytes)
-    {
-        getOrCreateSegmentList(segmentType).add(segmentBytes);
-    }
-
-    /**
-     * Gets the set of JPEG segment type identifiers.
-     */
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        Set<JpegSegmentType> segmentTypes = new HashSet<JpegSegmentType>();
-
-        for (Byte segmentTypeByte : _segmentDataMap.keySet())
-        {
-            JpegSegmentType segmentType = JpegSegmentType.fromByte(segmentTypeByte);
-            if (segmentType == null) {
-                throw new IllegalStateException("Should not have a segmentTypeByte that is not in the enum: " + Integer.toHexString(segmentTypeByte));
-            }
-            segmentTypes.add(segmentType);
-        }
-
-        return segmentTypes;
-    }
-
-    /**
-     * Gets the first JPEG segment data for the specified type.
-     *
-     * @param segmentType the JpegSegmentType for the desired segment
-     * @return a byte[] containing segment data or null if no data exists for that segment
-     */
-    @Nullable
-    public byte[] getSegment(byte segmentType)
-    {
-        return getSegment(segmentType, 0);
-    }
-
-    /**
-     * Gets the first JPEG segment data for the specified type.
-     *
-     * @param segmentType the JpegSegmentType for the desired segment
-     * @return a byte[] containing segment data or null if no data exists for that segment
-     */
-    @Nullable
-    public byte[] getSegment(@NotNull JpegSegmentType segmentType)
-    {
-        return getSegment(segmentType.byteValue, 0);
-    }
-
-    /**
-     * Gets segment data for a specific occurrence and type.  Use this method when more than one occurrence
-     * of segment data for a given type exists.
-     *
-     * @param segmentType identifies the required segment
-     * @param occurrence  the zero-based index of the occurrence
-     * @return the segment data as a byte[], or null if no segment exists for the type &amp; occurrence
-     */
-    @Nullable
-    public byte[] getSegment(@NotNull JpegSegmentType segmentType, int occurrence)
-    {
-        return getSegment(segmentType.byteValue, occurrence);
-    }
-
-    /**
-     * Gets segment data for a specific occurrence and type.  Use this method when more than one occurrence
-     * of segment data for a given type exists.
-     *
-     * @param segmentType identifies the required segment
-     * @param occurrence  the zero-based index of the occurrence
-     * @return the segment data as a byte[], or null if no segment exists for the type &amp; occurrence
-     */
-    @Nullable
-    public byte[] getSegment(byte segmentType, int occurrence)
-    {
-        final List<byte[]> segmentList = getSegmentList(segmentType);
-
-        return segmentList != null && segmentList.size() > occurrence
-                ? segmentList.get(occurrence)
-                : null;
-    }
-
-    /**
-     * Returns all instances of a given JPEG segment.  If no instances exist, an empty sequence is returned.
-     *
-     * @param segmentType a number which identifies the type of JPEG segment being queried
-     * @return zero or more byte arrays, each holding the data of a JPEG segment
-     */
-    @NotNull
-    public Iterable<byte[]> getSegments(@NotNull JpegSegmentType segmentType)
-    {
-        return getSegments(segmentType.byteValue);
-    }
-
-    /**
-     * Returns all instances of a given JPEG segment.  If no instances exist, an empty sequence is returned.
-     *
-     * @param segmentType a number which identifies the type of JPEG segment being queried
-     * @return zero or more byte arrays, each holding the data of a JPEG segment
-     */
-    @NotNull
-    public Iterable<byte[]> getSegments(byte segmentType)
-    {
-        final List<byte[]> segmentList = getSegmentList(segmentType);
-        return segmentList == null ? new ArrayList<byte[]>() : segmentList;
-    }
-
-    @Nullable
-    private List<byte[]> getSegmentList(byte segmentType)
-    {
-        return _segmentDataMap.get(segmentType);
-    }
-
-    @NotNull
-    private List<byte[]> getOrCreateSegmentList(byte segmentType)
-    {
-        List<byte[]> segmentList;
-        if (_segmentDataMap.containsKey(segmentType)) {
-            segmentList = _segmentDataMap.get(segmentType);
-        } else {
-            segmentList = new ArrayList<byte[]>();
-            _segmentDataMap.put(segmentType, segmentList);
-        }
-        return segmentList;
-    }
-
-    /**
-     * Returns the count of segment data byte arrays stored for a given segment type.
-     *
-     * @param segmentType identifies the required segment
-     * @return the segment count (zero if no segments exist).
-     */
-    public int getSegmentCount(@NotNull JpegSegmentType segmentType)
-    {
-        return getSegmentCount(segmentType.byteValue);
-    }
-
-    /**
-     * Returns the count of segment data byte arrays stored for a given segment type.
-     *
-     * @param segmentType identifies the required segment
-     * @return the segment count (zero if no segments exist).
-     */
-    public int getSegmentCount(byte segmentType)
-    {
-        final List<byte[]> segmentList = getSegmentList(segmentType);
-        return segmentList == null ? 0 : segmentList.size();
-    }
-
-    /**
-     * Removes a specified instance of a segment's data from the collection.  Use this method when more than one
-     * occurrence of segment data exists for a given type exists.
-     *
-     * @param segmentType identifies the required segment
-     * @param occurrence  the zero-based index of the segment occurrence to remove.
-     */
-    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
-    public void removeSegmentOccurrence(@NotNull JpegSegmentType segmentType, int occurrence)
-    {
-        removeSegmentOccurrence(segmentType.byteValue, occurrence);
-    }
-
-    /**
-     * Removes a specified instance of a segment's data from the collection.  Use this method when more than one
-     * occurrence of segment data exists for a given type exists.
-     *
-     * @param segmentType identifies the required segment
-     * @param occurrence  the zero-based index of the segment occurrence to remove.
-     */
-    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
-    public void removeSegmentOccurrence(byte segmentType, int occurrence)
-    {
-        final List<byte[]> segmentList = _segmentDataMap.get(segmentType);
-        segmentList.remove(occurrence);
-    }
-
-    /**
-     * Removes all segments from the collection having the specified type.
-     *
-     * @param segmentType identifies the required segment
-     */
-    public void removeSegment(@NotNull JpegSegmentType segmentType)
-    {
-        removeSegment(segmentType.byteValue);
-    }
-
-    /**
-     * Removes all segments from the collection having the specified type.
-     *
-     * @param segmentType identifies the required segment
-     */
-    public void removeSegment(byte segmentType)
-    {
-        _segmentDataMap.remove(segmentType);
-    }
-
-    /**
-     * Determines whether data is present for a given segment type.
-     *
-     * @param segmentType identifies the required segment
-     * @return true if data exists, otherwise false
-     */
-    public boolean containsSegment(@NotNull JpegSegmentType segmentType)
-    {
-        return containsSegment(segmentType.byteValue);
-    }
-
-    /**
-     * Determines whether data is present for a given segment type.
-     *
-     * @param segmentType identifies the required segment
-     * @return true if data exists, otherwise false
-     */
-    public boolean containsSegment(byte segmentType)
-    {
-        return _segmentDataMap.containsKey(segmentType);
-    }
-}
Index: unk/src/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java	(revision 16024)
+++ 	(revision )
@@ -1,26 +1,0 @@
-package com.drew.imaging.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-
-/**
- * Defines an object that extracts metadata from in JPEG segments.
- */
-public interface JpegSegmentMetadataReader
-{
-    /**
-     * Gets the set of JPEG segment types that this reader is interested in.
-     */
-    @NotNull
-    Iterable<JpegSegmentType> getSegmentTypes();
-
-    /**
-     * Extracts metadata from all instances of a particular JPEG segment type.
-     *
-     * @param segments A sequence of byte arrays from which the metadata should be extracted. These are in the order
-     *                 encountered in the original file.
-     * @param metadata The {@link Metadata} object into which extracted values should be merged.
-     * @param segmentType The {@link JpegSegmentType} being read.
-     */
-    void readJpegSegments(@NotNull final Iterable<byte[]> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType);
-}
Index: unk/src/com/drew/imaging/jpeg/JpegSegmentReader.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java	(revision 16024)
+++ 	(revision )
@@ -1,169 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.jpeg;
-
-import com.drew.lang.SequentialReader;
-import com.drew.lang.StreamReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Performs read functions of JPEG files, returning specific file segments.
- * <p>
- * JPEG files are composed of a sequence of consecutive JPEG 'segments'. Each is identified by one of a set of byte
- * values, modelled in the {@link JpegSegmentType} enumeration. Use <code>readSegments</code> to read out the some
- * or all segments into a {@link JpegSegmentData} object, from which the raw JPEG segment byte arrays may be accessed.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class JpegSegmentReader
-{
-    /**
-     * The 0xFF byte that signals the start of a segment.
-     */
-    private static final byte SEGMENT_IDENTIFIER = (byte) 0xFF;
-
-    /**
-     * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet).
-     */
-    private static final byte SEGMENT_SOS = (byte) 0xDA;
-
-    /**
-     * Private, because one wouldn't search for it.
-     */
-    private static final byte MARKER_EOI = (byte) 0xD9;
-
-    /**
-     * Processes the provided JPEG data, and extracts the specified JPEG segments into a {@link JpegSegmentData} object.
-     * <p>
-     * Will not return SOS (start of scan) or EOI (end of image) segments.
-     *
-     * @param file a {@link File} from which the JPEG data will be read.
-     * @param segmentTypes the set of JPEG segments types that are to be returned. If this argument is <code>null</code>
-     *                     then all found segment types are returned.
-     */
-    @NotNull
-    public static JpegSegmentData readSegments(@NotNull File file, @Nullable Iterable<JpegSegmentType> segmentTypes) throws JpegProcessingException, IOException
-    {
-        FileInputStream stream = null;
-        try {
-            stream = new FileInputStream(file);
-            return readSegments(new StreamReader(stream), segmentTypes);
-        } finally {
-            if (stream != null) {
-                stream.close();
-            }
-        }
-    }
-
-    /**
-     * Processes the provided JPEG data, and extracts the specified JPEG segments into a {@link JpegSegmentData} object.
-     * <p>
-     * Will not return SOS (start of scan) or EOI (end of image) segments.
-     *
-     * @param reader a {@link SequentialReader} from which the JPEG data will be read. It must be positioned at the
-     *               beginning of the JPEG data stream.
-     * @param segmentTypes the set of JPEG segments types that are to be returned. If this argument is <code>null</code>
-     *                     then all found segment types are returned.
-     */
-    @NotNull
-    public static JpegSegmentData readSegments(@NotNull final SequentialReader reader, @Nullable Iterable<JpegSegmentType> segmentTypes) throws JpegProcessingException, IOException
-    {
-        // Must be big-endian
-        assert (reader.isMotorolaByteOrder());
-
-        // first two bytes should be JPEG magic number
-        final int magicNumber = reader.getUInt16();
-        if (magicNumber != 0xFFD8) {
-            throw new JpegProcessingException("JPEG data is expected to begin with 0xFFD8 (ÿØ) not 0x" + Integer.toHexString(magicNumber));
-        }
-
-        Set<Byte> segmentTypeBytes = null;
-        if (segmentTypes != null) {
-            segmentTypeBytes = new HashSet<Byte>();
-            for (JpegSegmentType segmentType : segmentTypes) {
-                segmentTypeBytes.add(segmentType.byteValue);
-            }
-        }
-
-        JpegSegmentData segmentData = new JpegSegmentData();
-
-        do {
-            // Find the segment marker. Markers are zero or more 0xFF bytes, followed
-            // by a 0xFF and then a byte not equal to 0x00 or 0xFF.
-
-            byte segmentIdentifier = reader.getInt8();
-            byte segmentType = reader.getInt8();
-
-            // Read until we have a 0xFF byte followed by a byte that is not 0xFF or 0x00
-            while (segmentIdentifier != SEGMENT_IDENTIFIER || segmentType == SEGMENT_IDENTIFIER || segmentType == 0) {
-            	segmentIdentifier = segmentType;
-            	segmentType = reader.getInt8();
-            }
-
-            if (segmentType == SEGMENT_SOS) {
-                // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would
-                // have to search for the two bytes: 0xFF 0xD9 (EOI).
-                // It comes last so simply return at this point
-                return segmentData;
-            }
-
-            if (segmentType == MARKER_EOI) {
-                // the 'End-Of-Image' segment -- this should never be found in this fashion
-                return segmentData;
-            }
-
-            // next 2-bytes are <segment-size>: [high-byte] [low-byte]
-            int segmentLength = reader.getUInt16();
-
-            // segment length includes size bytes, so subtract two
-            segmentLength -= 2;
-
-            if (segmentLength < 0)
-                throw new JpegProcessingException("JPEG segment size would be less than zero");
-
-            // Check whether we are interested in this segment
-            if (segmentTypeBytes == null || segmentTypeBytes.contains(segmentType)) {
-                byte[] segmentBytes = reader.getBytes(segmentLength);
-                assert (segmentLength == segmentBytes.length);
-                segmentData.addSegment(segmentType, segmentBytes);
-            } else {
-                // Skip this segment
-                if (!reader.trySkip(segmentLength)) {
-                    // If skipping failed, just return the segments we found so far
-                    return segmentData;
-                }
-            }
-
-        } while (true);
-    }
-
-    private JpegSegmentReader() throws Exception
-    {
-        throw new Exception("Not intended for instantiation.");
-    }
-}
Index: unk/src/com/drew/imaging/jpeg/JpegSegmentType.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/JpegSegmentType.java	(revision 16024)
+++ 	(revision )
@@ -1,194 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.jpeg;
-
-import com.drew.lang.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * An enumeration of the known segment types found in JPEG files.
- *
- * <ul>
- *     <li>http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html</li>
- *     <li>http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html</li>
- * </ul>
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public enum JpegSegmentType
-{
-    /** APP0 JPEG segment identifier. Commonly contains JFIF, JFXX. */
-    APP0((byte)0xE0, true),
-
-    /** APP1 JPEG segment identifier. Commonly contains Exif. XMP data is also kept in here, though usually in a second instance. */
-    APP1((byte)0xE1, true),
-
-    /** APP2 JPEG segment identifier. Commonly contains ICC. */
-    APP2((byte)0xE2, true),
-
-    /** APP3 JPEG segment identifier. */
-    APP3((byte)0xE3, true),
-
-    /** APP4 JPEG segment identifier. */
-    APP4((byte)0xE4, true),
-
-    /** APP5 JPEG segment identifier. */
-    APP5((byte)0xE5, true),
-
-    /** APP6 JPEG segment identifier. */
-    APP6((byte)0xE6, true),
-
-    /** APP7 JPEG segment identifier. */
-    APP7((byte)0xE7, true),
-
-    /** APP8 JPEG segment identifier. */
-    APP8((byte)0xE8, true),
-
-    /** APP9 JPEG segment identifier. */
-    APP9((byte)0xE9, true),
-
-    /** APPA (App10) JPEG segment identifier. Can contain Unicode comments, though {@link JpegSegmentType#COM} is more commonly used for comments. */
-    APPA((byte)0xEA, true),
-
-    /** APPB (App11) JPEG segment identifier. */
-    APPB((byte)0xEB, true),
-
-    /** APPC (App12) JPEG segment identifier. */
-    APPC((byte)0xEC, true),
-
-    /** APPD (App13) JPEG segment identifier. Commonly contains IPTC, Photoshop data. */
-    APPD((byte)0xED, true),
-
-    /** APPE (App14) JPEG segment identifier. Commonly contains Adobe data. */
-    APPE((byte)0xEE, true),
-
-    /** APPF (App15) JPEG segment identifier. */
-    APPF((byte)0xEF, true),
-
-    /** Start Of Image segment identifier. */
-    SOI((byte)0xD8, false),
-
-    /** Define Quantization Table segment identifier. */
-    DQT((byte)0xDB, false),
-
-    /** Define Number of Lines segment identifier. */
-    DNL((byte)0xDC, false),
-
-    /** Define Restart Interval segment identifier. */
-    DRI((byte)0xDD, false),
-
-    /** Define Hierarchical Progression segment identifier. */
-    DHP((byte)0xDE, false),
-
-    /** EXPand reference component(s) segment identifier. */
-    EXP((byte)0xDF, false),
-
-    /** Define Huffman Table segment identifier. */
-    DHT((byte)0xC4, false),
-
-    /** Define Arithmetic Coding conditioning segment identifier. */
-    DAC((byte)0xCC, false),
-
-    /** Start-of-Frame (0) segment identifier for Baseline DCT. */
-    SOF0((byte)0xC0, true),
-
-    /** Start-of-Frame (1) segment identifier for Extended sequential DCT. */
-    SOF1((byte)0xC1, true),
-
-    /** Start-of-Frame (2) segment identifier for Progressive DCT. */
-    SOF2((byte)0xC2, true),
-
-    /** Start-of-Frame (3) segment identifier for Lossless (sequential). */
-    SOF3((byte)0xC3, true),
-
-//    /** Start-of-Frame (4) segment identifier. */
-//    SOF4((byte)0xC4, true),
-
-    /** Start-of-Frame (5) segment identifier for Differential sequential DCT. */
-    SOF5((byte)0xC5, true),
-
-    /** Start-of-Frame (6) segment identifier for Differential progressive DCT. */
-    SOF6((byte)0xC6, true),
-
-    /** Start-of-Frame (7) segment identifier for Differential lossless (sequential). */
-    SOF7((byte)0xC7, true),
-
-    /** Reserved for JPEG extensions. */
-    JPG((byte)0xC8, true),
-
-    /** Start-of-Frame (9) segment identifier for Extended sequential DCT. */
-    SOF9((byte)0xC9, true),
-
-    /** Start-of-Frame (10) segment identifier for Progressive DCT. */
-    SOF10((byte)0xCA, true),
-
-    /** Start-of-Frame (11) segment identifier for Lossless (sequential). */
-    SOF11((byte)0xCB, true),
-
-//    /** Start-of-Frame (12) segment identifier. */
-//    SOF12((byte)0xCC, true),
-
-    /** Start-of-Frame (13) segment identifier for Differential sequential DCT. */
-    SOF13((byte)0xCD, true),
-
-    /** Start-of-Frame (14) segment identifier for Differential progressive DCT. */
-    SOF14((byte)0xCE, true),
-
-    /** Start-of-Frame (15) segment identifier for Differential lossless (sequential). */
-    SOF15((byte)0xCF, true),
-
-    /** JPEG comment segment identifier for comments. */
-    COM((byte)0xFE, true);
-
-    public static final Collection<JpegSegmentType> canContainMetadataTypes;
-
-    static {
-        List<JpegSegmentType> segmentTypes = new ArrayList<JpegSegmentType>();
-        for (JpegSegmentType segmentType : JpegSegmentType.class.getEnumConstants()) {
-            if (segmentType.canContainMetadata) {
-                segmentTypes.add(segmentType);
-            }
-        }
-        canContainMetadataTypes = segmentTypes;
-    }
-
-    public final byte byteValue;
-    public final boolean canContainMetadata;
-
-    JpegSegmentType(byte byteValue, boolean canContainMetadata)
-    {
-        this.byteValue = byteValue;
-        this.canContainMetadata = canContainMetadata;
-    }
-
-    @Nullable
-    public static JpegSegmentType fromByte(byte segmentTypeByte)
-    {
-        for (JpegSegmentType segmentType : JpegSegmentType.class.getEnumConstants()) {
-            if (segmentType.byteValue == segmentTypeByte)
-                return segmentType;
-        }
-        return null;
-    }
-}
Index: unk/src/com/drew/imaging/jpeg/package-info.java
===================================================================
--- /trunk/src/com/drew/imaging/jpeg/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for working with JPEG files.
- */
-package com.drew.imaging.jpeg;
Index: unk/src/com/drew/imaging/package-info.java
===================================================================
--- /trunk/src/com/drew/imaging/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,5 +1,0 @@
-/**
- * Contains classes for working with image file formats and photographic conversions.
- * <!-- Put @see and @since tags down here. -->
- */
-package com.drew.imaging;
Index: unk/src/com/drew/imaging/tiff/TiffDataFormat.java
===================================================================
--- /trunk/src/com/drew/imaging/tiff/TiffDataFormat.java	(revision 16024)
+++ 	(revision )
@@ -1,107 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.tiff;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * An enumeration of data formats used by the TIFF specification.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class TiffDataFormat
-{
-    public static final int CODE_INT8_U = 1;
-    public static final int CODE_STRING = 2;
-    public static final int CODE_INT16_U = 3;
-    public static final int CODE_INT32_U = 4;
-    public static final int CODE_RATIONAL_U = 5;
-    public static final int CODE_INT8_S = 6;
-    public static final int CODE_UNDEFINED = 7;
-    public static final int CODE_INT16_S = 8;
-    public static final int CODE_INT32_S = 9;
-    public static final int CODE_RATIONAL_S = 10;
-    public static final int CODE_SINGLE = 11;
-    public static final int CODE_DOUBLE = 12;
-
-    @NotNull public static final TiffDataFormat INT8_U = new TiffDataFormat("BYTE", CODE_INT8_U, 1);
-    @NotNull public static final TiffDataFormat STRING = new TiffDataFormat("STRING", CODE_STRING, 1);
-    @NotNull public static final TiffDataFormat INT16_U = new TiffDataFormat("USHORT", CODE_INT16_U, 2);
-    @NotNull public static final TiffDataFormat INT32_U = new TiffDataFormat("ULONG", CODE_INT32_U, 4);
-    @NotNull public static final TiffDataFormat RATIONAL_U = new TiffDataFormat("URATIONAL", CODE_RATIONAL_U, 8);
-    @NotNull public static final TiffDataFormat INT8_S = new TiffDataFormat("SBYTE", CODE_INT8_S, 1);
-    @NotNull public static final TiffDataFormat UNDEFINED = new TiffDataFormat("UNDEFINED", CODE_UNDEFINED, 1);
-    @NotNull public static final TiffDataFormat INT16_S = new TiffDataFormat("SSHORT", CODE_INT16_S, 2);
-    @NotNull public static final TiffDataFormat INT32_S = new TiffDataFormat("SLONG", CODE_INT32_S, 4);
-    @NotNull public static final TiffDataFormat RATIONAL_S = new TiffDataFormat("SRATIONAL", CODE_RATIONAL_S, 8);
-    @NotNull public static final TiffDataFormat SINGLE = new TiffDataFormat("SINGLE", CODE_SINGLE, 4);
-    @NotNull public static final TiffDataFormat DOUBLE = new TiffDataFormat("DOUBLE", CODE_DOUBLE, 8);
-
-    @NotNull
-    private final String _name;
-    private final int _tiffFormatCode;
-    private final int _componentSizeBytes;
-
-    @Nullable
-    public static TiffDataFormat fromTiffFormatCode(int tiffFormatCode)
-    {
-        switch (tiffFormatCode) {
-            case 1: return INT8_U;
-            case 2: return STRING;
-            case 3: return INT16_U;
-            case 4: return INT32_U;
-            case 5: return RATIONAL_U;
-            case 6: return INT8_S;
-            case 7: return UNDEFINED;
-            case 8: return INT16_S;
-            case 9: return INT32_S;
-            case 10: return RATIONAL_S;
-            case 11: return SINGLE;
-            case 12: return DOUBLE;
-        }
-        return null;
-    }
-
-    private TiffDataFormat(@NotNull String name, int tiffFormatCode, int componentSizeBytes)
-    {
-        _name = name;
-        _tiffFormatCode = tiffFormatCode;
-        _componentSizeBytes = componentSizeBytes;
-    }
-
-    public int getComponentSizeBytes()
-    {
-        return _componentSizeBytes;
-    }
-
-    public int getTiffFormatCode()
-    {
-        return _tiffFormatCode;
-    }
-
-    @Override
-    @NotNull
-    public String toString()
-    {
-        return _name;
-    }
-}
Index: unk/src/com/drew/imaging/tiff/TiffHandler.java
===================================================================
--- /trunk/src/com/drew/imaging/tiff/TiffHandler.java	(revision 16024)
+++ 	(revision )
@@ -1,88 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.tiff;
-
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.StringValue;
-
-import java.io.IOException;
-import java.util.Set;
-
-/**
- * Interface of an class capable of handling events raised during the reading of a TIFF file
- * via {@link TiffReader}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public interface TiffHandler
-{
-    /**
-     * Receives the 2-byte marker found in the TIFF header.
-     * <p>
-     * Implementations are not obligated to use this information for any purpose, though it may be useful for
-     * validation or perhaps differentiating the type of mapping to use for observed tags and IFDs.
-     *
-     * @param marker the 2-byte value found at position 2 of the TIFF header
-     */
-    void setTiffMarker(int marker) throws TiffProcessingException;
-
-    boolean tryEnterSubIfd(int tagId);
-    boolean hasFollowerIfd();
-
-    void endingIFD();
-
-    @Nullable
-    Long tryCustomProcessFormat(int tagId, int formatCode, long componentCount);
-
-    boolean customProcessTag(int tagOffset,
-                             @NotNull Set<Integer> processedIfdOffsets,
-                             int tiffHeaderOffset,
-                             @NotNull RandomAccessReader reader,
-                             int tagId,
-                             int byteCount) throws IOException;
-
-    void warn(@NotNull String message);
-    void error(@NotNull String message);
-
-    void setByteArray(int tagId, @NotNull byte[] bytes);
-    void setString(int tagId, @NotNull StringValue string);
-    void setRational(int tagId, @NotNull Rational rational);
-    void setRationalArray(int tagId, @NotNull Rational[] array);
-    void setFloat(int tagId, float float32);
-    void setFloatArray(int tagId, @NotNull float[] array);
-    void setDouble(int tagId, double double64);
-    void setDoubleArray(int tagId, @NotNull double[] array);
-    void setInt8s(int tagId, byte int8s);
-    void setInt8sArray(int tagId, @NotNull byte[] array);
-    void setInt8u(int tagId, short int8u);
-    void setInt8uArray(int tagId, @NotNull short[] array);
-    void setInt16s(int tagId, int int16s);
-    void setInt16sArray(int tagId, @NotNull short[] array);
-    void setInt16u(int tagId, int int16u);
-    void setInt16uArray(int tagId, @NotNull int[] array);
-    void setInt32s(int tagId, int int32s);
-    void setInt32sArray(int tagId, @NotNull int[] array);
-    void setInt32u(int tagId, long int32u);
-    void setInt32uArray(int tagId, @NotNull long[] array);
-}
Index: unk/src/com/drew/imaging/tiff/TiffProcessingException.java
===================================================================
--- /trunk/src/com/drew/imaging/tiff/TiffProcessingException.java	(revision 16024)
+++ 	(revision )
@@ -1,51 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.imaging.tiff;
-
-import com.drew.imaging.ImageProcessingException;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * An exception class thrown upon unexpected and fatal conditions while processing a TIFF file.
- *
- * @author Drew Noakes https://drewnoakes.com
- * @author Darren Salomons
- */
-public class TiffProcessingException extends ImageProcessingException
-{
-    private static final long serialVersionUID = -1658134119488001891L;
-
-    public TiffProcessingException(@Nullable String message)
-    {
-        super(message);
-    }
-
-    public TiffProcessingException(@Nullable String message, @Nullable Throwable cause)
-    {
-        super(message, cause);
-    }
-
-    public TiffProcessingException(@Nullable Throwable cause)
-    {
-        super(cause);
-    }
-}
Index: unk/src/com/drew/imaging/tiff/TiffReader.java
===================================================================
--- /trunk/src/com/drew/imaging/tiff/TiffReader.java	(revision 16024)
+++ 	(revision )
@@ -1,386 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.imaging.tiff;
-
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Processes TIFF-formatted data, calling into client code via that {@link TiffHandler} interface.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class TiffReader
-{
-    /**
-     * Processes a TIFF data sequence.
-     *
-     * @param reader the {@link RandomAccessReader} from which the data should be read
-     * @param handler the {@link TiffHandler} that will coordinate processing and accept read values
-     * @param tiffHeaderOffset the offset within <code>reader</code> at which the TIFF header starts
-     * @throws TiffProcessingException if an error occurred during the processing of TIFF data that could not be
-     *                                 ignored or recovered from
-     * @throws IOException an error occurred while accessing the required data
-     */
-    public void processTiff(@NotNull final RandomAccessReader reader,
-                            @NotNull final TiffHandler handler,
-                            final int tiffHeaderOffset) throws TiffProcessingException, IOException
-    {
-        // This must be either "MM" or "II".
-        short byteOrderIdentifier = reader.getInt16(tiffHeaderOffset);
-
-        if (byteOrderIdentifier == 0x4d4d) { // "MM"
-            reader.setMotorolaByteOrder(true);
-        } else if (byteOrderIdentifier == 0x4949) { // "II"
-            reader.setMotorolaByteOrder(false);
-        } else {
-            throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
-        }
-
-        // Check the next two values for correctness.
-        final int tiffMarker = reader.getUInt16(2 + tiffHeaderOffset);
-        handler.setTiffMarker(tiffMarker);
-
-        int firstIfdOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset;
-
-        // David Ekholm sent a digital camera image that has this problem
-        // TODO getLength should be avoided as it causes RandomAccessStreamReader to read to the end of the stream
-        if (firstIfdOffset >= reader.getLength() - 1) {
-            handler.warn("First IFD offset is beyond the end of the TIFF data segment -- trying default offset");
-            // First directory normally starts immediately after the offset bytes, so try that
-            firstIfdOffset = tiffHeaderOffset + 2 + 2 + 4;
-        }
-
-        Set<Integer> processedIfdOffsets = new HashSet<Integer>();
-        processIfd(handler, reader, processedIfdOffsets, firstIfdOffset, tiffHeaderOffset);
-    }
-
-    /**
-     * Processes a TIFF IFD.
-     *
-     * IFD Header:
-     * <ul>
-     *     <li><b>2 bytes</b> number of tags</li>
-     * </ul>
-     * Tag structure:
-     * <ul>
-     *     <li><b>2 bytes</b> tag type</li>
-     *     <li><b>2 bytes</b> format code (values 1 to 12, inclusive)</li>
-     *     <li><b>4 bytes</b> component count</li>
-     *     <li><b>4 bytes</b> inline value, or offset pointer if too large to fit in four bytes</li>
-     * </ul>
-     *
-     *
-     * @param handler the {@link com.drew.imaging.tiff.TiffHandler} that will coordinate processing and accept read values
-     * @param reader the {@link com.drew.lang.RandomAccessReader} from which the data should be read
-     * @param processedIfdOffsets the set of visited IFD offsets, to avoid revisiting the same IFD in an endless loop
-     * @param ifdOffset the offset within <code>reader</code> at which the IFD data starts
-     * @param tiffHeaderOffset the offset within <code>reader</code> at which the TIFF header starts
-     * @throws IOException an error occurred while accessing the required data
-     */
-    public static void processIfd(@NotNull final TiffHandler handler,
-                                  @NotNull final RandomAccessReader reader,
-                                  @NotNull final Set<Integer> processedIfdOffsets,
-                                  final int ifdOffset,
-                                  final int tiffHeaderOffset) throws IOException
-    {
-        Boolean resetByteOrder = null;
-        try {
-            // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
-            if (processedIfdOffsets.contains(Integer.valueOf(ifdOffset))) {
-                return;
-            }
-
-            // remember that we've visited this directory so that we don't visit it again later
-            processedIfdOffsets.add(ifdOffset);
-
-            if (ifdOffset >= reader.getLength() || ifdOffset < 0) {
-                handler.error("Ignored IFD marked to start outside data segment");
-                return;
-            }
-
-            // First two bytes in the IFD are the number of tags in this directory
-            int dirTagCount = reader.getUInt16(ifdOffset);
-
-            // Some software modifies the byte order of the file, but misses some IFDs (such as makernotes).
-            // The entire test image repository doesn't contain a single IFD with more than 255 entries.
-            // Here we detect switched bytes that suggest this problem, and temporarily swap the byte order.
-            // This was discussed in GitHub issue #136.
-            if (dirTagCount > 0xFF && (dirTagCount & 0xFF) == 0) {
-                resetByteOrder = reader.isMotorolaByteOrder();
-                dirTagCount >>= 8;
-                reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder());
-            }
-
-            int dirLength = (2 + (12 * dirTagCount) + 4);
-            if (dirLength + ifdOffset > reader.getLength()) {
-                handler.error("Illegally sized IFD");
-                return;
-            }
-
-            //
-            // Handle each tag in this directory
-            //
-            int invalidTiffFormatCodeCount = 0;
-            for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) {
-                final int tagOffset = calculateTagOffset(ifdOffset, tagNumber);
-
-                // 2 bytes for the tag id
-                final int tagId = reader.getUInt16(tagOffset);
-
-                // 2 bytes for the format code
-                final int formatCode = reader.getUInt16(tagOffset + 2);
-                final TiffDataFormat format = TiffDataFormat.fromTiffFormatCode(formatCode);
-
-                // 4 bytes dictate the number of components in this tag's data
-                final long componentCount = reader.getUInt32(tagOffset + 4);
-
-                final long byteCount;
-                if (format == null) {
-                    Long byteCountOverride = handler.tryCustomProcessFormat(tagId, formatCode, componentCount);
-                    if (byteCountOverride == null) {
-                        // This error suggests that we are processing at an incorrect index and will generate
-                        // rubbish until we go out of bounds (which may be a while).  Exit now.
-                        handler.error(String.format("Invalid TIFF tag format code %d for tag 0x%04X", formatCode, tagId));
-                        // TODO specify threshold as a parameter, or provide some other external control over this behaviour
-                        if (++invalidTiffFormatCodeCount > 5) {
-                            handler.error("Stopping processing as too many errors seen in TIFF IFD");
-                            return;
-                        }
-                        continue;
-                    }
-                    byteCount = byteCountOverride;
-                } else {
-                    byteCount = componentCount * format.getComponentSizeBytes();
-                }
-
-                final long tagValueOffset;
-                if (byteCount > 4) {
-                    // If it's bigger than 4 bytes, the dir entry contains an offset.
-                    final long offsetVal = reader.getUInt32(tagOffset + 8);
-                    if (offsetVal + byteCount > reader.getLength()) {
-                        // Bogus pointer offset and / or byteCount value
-                        handler.error("Illegal TIFF tag pointer offset");
-                        continue;
-                    }
-                    tagValueOffset = tiffHeaderOffset + offsetVal;
-                } else {
-                    // 4 bytes or less and value is in the dir entry itself.
-                    tagValueOffset = tagOffset + 8;
-                }
-
-                if (tagValueOffset < 0 || tagValueOffset > reader.getLength()) {
-                    handler.error("Illegal TIFF tag pointer offset");
-                    continue;
-                }
-
-                // Check that this tag isn't going to allocate outside the bounds of the data array.
-                // This addresses an uncommon OutOfMemoryError.
-                if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) {
-                    handler.error("Illegal number of bytes for TIFF tag data: " + byteCount);
-                    continue;
-                }
-
-                // Some tags point to one or more additional IFDs to process
-                boolean isIfdPointer = false;
-                if (byteCount == 4 * componentCount) {
-                    for (int i = 0; i < componentCount; i++) {
-                        if (handler.tryEnterSubIfd(tagId)) {
-                            isIfdPointer = true;
-                            int subDirOffset = tiffHeaderOffset + reader.getInt32((int) (tagValueOffset + i * 4));
-                            processIfd(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset);
-                        }
-                    }
-                }
-
-                // If it wasn't an IFD pointer, allow custom tag processing to occur
-                if (!isIfdPointer && !handler.customProcessTag((int) tagValueOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, (int) byteCount)) {
-                    // If no custom processing occurred, process the tag in the standard fashion
-                    processTag(handler, tagId, (int) tagValueOffset, (int) componentCount, formatCode, reader);
-                }
-            }
-
-            // at the end of each IFD is an optional link to the next IFD
-            final int finalTagOffset = calculateTagOffset(ifdOffset, dirTagCount);
-            int nextIfdOffset = reader.getInt32(finalTagOffset);
-            if (nextIfdOffset != 0) {
-                nextIfdOffset += tiffHeaderOffset;
-                if (nextIfdOffset >= reader.getLength()) {
-                    // Last 4 bytes of IFD reference another IFD with an address that is out of bounds
-                    // Note this could have been caused by jhead 1.3 cropping too much
-                    return;
-                } else if (nextIfdOffset < ifdOffset) {
-                    // TODO is this a valid restriction?
-                    // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
-                    return;
-                }
-
-                if (handler.hasFollowerIfd()) {
-                    processIfd(handler, reader, processedIfdOffsets, nextIfdOffset, tiffHeaderOffset);
-                }
-            }
-        } finally {
-            handler.endingIFD();
-            if (resetByteOrder != null)
-                reader.setMotorolaByteOrder(resetByteOrder);
-        }
-    }
-
-    private static void processTag(@NotNull final TiffHandler handler,
-                                   final int tagId,
-                                   final int tagValueOffset,
-                                   final int componentCount,
-                                   final int formatCode,
-                                   @NotNull final RandomAccessReader reader) throws IOException
-    {
-        switch (formatCode) {
-            case TiffDataFormat.CODE_UNDEFINED:
-                // this includes exif user comments
-                handler.setByteArray(tagId, reader.getBytes(tagValueOffset, componentCount));
-                break;
-            case TiffDataFormat.CODE_STRING:
-                handler.setString(tagId, reader.getNullTerminatedStringValue(tagValueOffset, componentCount, null));
-                break;
-            case TiffDataFormat.CODE_RATIONAL_S:
-                if (componentCount == 1) {
-                    handler.setRational(tagId, new Rational(reader.getInt32(tagValueOffset), reader.getInt32(tagValueOffset + 4)));
-                } else if (componentCount > 1) {
-                    Rational[] array = new Rational[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = new Rational(reader.getInt32(tagValueOffset + (8 * i)), reader.getInt32(tagValueOffset + 4 + (8 * i)));
-                    handler.setRationalArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_RATIONAL_U:
-                if (componentCount == 1) {
-                    handler.setRational(tagId, new Rational(reader.getUInt32(tagValueOffset), reader.getUInt32(tagValueOffset + 4)));
-                } else if (componentCount > 1) {
-                    Rational[] array = new Rational[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = new Rational(reader.getUInt32(tagValueOffset + (8 * i)), reader.getUInt32(tagValueOffset + 4 + (8 * i)));
-                    handler.setRationalArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_SINGLE:
-                if (componentCount == 1) {
-                    handler.setFloat(tagId, reader.getFloat32(tagValueOffset));
-                } else {
-                    float[] array = new float[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getFloat32(tagValueOffset + (i * 4));
-                    handler.setFloatArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_DOUBLE:
-                if (componentCount == 1) {
-                    handler.setDouble(tagId, reader.getDouble64(tagValueOffset));
-                } else {
-                    double[] array = new double[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getDouble64(tagValueOffset + (i * 4));
-                    handler.setDoubleArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_INT8_S:
-                if (componentCount == 1) {
-                    handler.setInt8s(tagId, reader.getInt8(tagValueOffset));
-                } else {
-                    byte[] array = new byte[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getInt8(tagValueOffset + i);
-                    handler.setInt8sArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_INT8_U:
-                if (componentCount == 1) {
-                    handler.setInt8u(tagId, reader.getUInt8(tagValueOffset));
-                } else {
-                    short[] array = new short[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getUInt8(tagValueOffset + i);
-                    handler.setInt8uArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_INT16_S:
-                if (componentCount == 1) {
-                    handler.setInt16s(tagId, (int)reader.getInt16(tagValueOffset));
-                } else {
-                    short[] array = new short[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getInt16(tagValueOffset + (i * 2));
-                    handler.setInt16sArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_INT16_U:
-                if (componentCount == 1) {
-                    handler.setInt16u(tagId, reader.getUInt16(tagValueOffset));
-                } else {
-                    int[] array = new int[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getUInt16(tagValueOffset + (i * 2));
-                    handler.setInt16uArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_INT32_S:
-                // NOTE 'long' in this case means 32 bit, not 64
-                if (componentCount == 1) {
-                    handler.setInt32s(tagId, reader.getInt32(tagValueOffset));
-                } else {
-                    int[] array = new int[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getInt32(tagValueOffset + (i * 4));
-                    handler.setInt32sArray(tagId, array);
-                }
-                break;
-            case TiffDataFormat.CODE_INT32_U:
-                // NOTE 'long' in this case means 32 bit, not 64
-                if (componentCount == 1) {
-                    handler.setInt32u(tagId, reader.getUInt32(tagValueOffset));
-                } else {
-                    long[] array = new long[componentCount];
-                    for (int i = 0; i < componentCount; i++)
-                        array[i] = reader.getUInt32(tagValueOffset + (i * 4));
-                    handler.setInt32uArray(tagId, array);
-                }
-                break;
-            default:
-                handler.error(String.format("Invalid TIFF tag format code %d for tag 0x%04X", formatCode, tagId));
-        }
-    }
-
-    /**
-     * Determine the offset of a given tag within the specified IFD.
-     *
-     * @param ifdStartOffset the offset at which the IFD starts
-     * @param entryNumber    the zero-based entry number
-     */
-    private static int calculateTagOffset(int ifdStartOffset, int entryNumber)
-    {
-        // Add 2 bytes for the tag count.
-        // Each entry is 12 bytes.
-        return ifdStartOffset + 2 + (12 * entryNumber);
-    }
-}
Index: unk/src/com/drew/imaging/tiff/package-info.java
===================================================================
--- /trunk/src/com/drew/imaging/tiff/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for working with TIFF format files.
- */
-package com.drew.imaging.tiff;
Index: unk/src/com/drew/lang/BufferBoundsException.java
===================================================================
--- /trunk/src/com/drew/lang/BufferBoundsException.java	(revision 16024)
+++ 	(revision )
@@ -1,59 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import java.io.IOException;
-
-/**
- * A checked replacement for {@link IndexOutOfBoundsException}.  Used by {@link RandomAccessReader}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public final class BufferBoundsException extends IOException
-{
-    private static final long serialVersionUID = 2911102837808946396L;
-
-    public BufferBoundsException(int index, int bytesRequested, long bufferLength)
-    {
-        super(getMessage(index, bytesRequested, bufferLength));
-    }
-
-    public BufferBoundsException(final String message)
-    {
-        super(message);
-    }
-
-    private static String getMessage(int index, int bytesRequested, long bufferLength)
-    {
-        if (index < 0)
-            return String.format("Attempt to read from buffer using a negative index (%d)", index);
-
-        if (bytesRequested < 0)
-            return String.format("Number of requested bytes cannot be negative (%d)", bytesRequested);
-
-        if ((long)index + (long)bytesRequested - 1L > (long)Integer.MAX_VALUE)
-            return String.format("Number of requested bytes summed with starting index exceed maximum range of signed 32 bit integers (requested index: %d, requested count: %d)", index, bytesRequested);
-
-        return String.format("Attempt to read from beyond end of underlying data source (requested index: %d, requested count: %d, max index: %d)",
-                index, bytesRequested, bufferLength - 1);
-    }
-}
Index: unk/src/com/drew/lang/ByteArrayReader.java
===================================================================
--- /trunk/src/com/drew/lang/ByteArrayReader.java	(revision 16024)
+++ 	(revision )
@@ -1,107 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.io.IOException;
-
-/**
- * Provides methods to read specific values from a byte array, with a consistent, checked exception structure for
- * issues.
- * <p>
- * By default, the reader operates with Motorola byte order (big endianness).  This can be changed by calling
- * <code>setMotorolaByteOrder(boolean)</code>.
- *
- * @author Drew Noakes https://drewnoakes.com
- * */
-public class ByteArrayReader extends RandomAccessReader
-{
-    @NotNull
-    private final byte[] _buffer;
-    private final int _baseOffset;
-
-    @SuppressWarnings({ "ConstantConditions" })
-    @com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
-    public ByteArrayReader(@NotNull byte[] buffer)
-    {
-        this(buffer, 0);
-    }
-
-    @SuppressWarnings({ "ConstantConditions" })
-    @com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent")
-    public ByteArrayReader(@NotNull byte[] buffer, int baseOffset)
-    {
-        if (buffer == null)
-            throw new NullPointerException();
-        if (baseOffset < 0)
-            throw new IllegalArgumentException("Must be zero or greater");
-
-        _buffer = buffer;
-        _baseOffset = baseOffset;
-    }
-
-    @Override
-    public int toUnshiftedOffset(int localOffset)
-    {
-        return localOffset + _baseOffset;
-    }
-
-    @Override
-    public long getLength()
-    {
-        return _buffer.length - _baseOffset;
-    }
-
-    @Override
-    public byte getByte(int index) throws IOException
-    {
-        validateIndex(index, 1);
-        return _buffer[index + _baseOffset];
-    }
-
-    @Override
-    protected void validateIndex(int index, int bytesRequested) throws IOException
-    {
-        if (!isValidIndex(index, bytesRequested))
-            throw new BufferBoundsException(toUnshiftedOffset(index), bytesRequested, _buffer.length);
-    }
-
-    @Override
-    protected boolean isValidIndex(int index, int bytesRequested) throws IOException
-    {
-        return bytesRequested >= 0
-            && index >= 0
-            && (long)index + (long)bytesRequested - 1L < getLength();
-    }
-
-    @Override
-    @NotNull
-    public byte[] getBytes(int index, int count) throws IOException
-    {
-        validateIndex(index, count);
-
-        byte[] bytes = new byte[count];
-        System.arraycopy(_buffer, index + _baseOffset, bytes, 0, count);
-        return bytes;
-    }
-}
Index: unk/src/com/drew/lang/Charsets.java
===================================================================
--- /trunk/src/com/drew/lang/Charsets.java	(revision 16024)
+++ 	(revision )
@@ -1,41 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.lang;
-
-import java.nio.charset.Charset;
-
-/**
- * Holds a set of commonly used character encodings.
- *
- * Newer JDKs include java.nio.charset.StandardCharsets, but we cannot use that in this library.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public final class Charsets
-{
-    public static final Charset UTF_8 = Charset.forName("UTF-8");
-    public static final Charset UTF_16 = Charset.forName("UTF-16");
-    public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
-    public static final Charset ASCII = Charset.forName("US-ASCII");
-    public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
-    public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
-    public static final Charset WINDOWS_1252 = Charset.forName("Cp1252");
-}
Index: unk/src/com/drew/lang/CompoundException.java
===================================================================
--- /trunk/src/com/drew/lang/CompoundException.java	(revision 16024)
+++ 	(revision )
@@ -1,109 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.io.PrintStream;
-import java.io.PrintWriter;
-
-/**
- * Represents a compound exception, as modelled in JDK 1.4, but
- * unavailable in previous versions.  This class allows support
- * of these previous JDK versions.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class CompoundException extends Exception
-{
-    private static final long serialVersionUID = -9207883813472069925L;
-
-    @Nullable
-    private final Throwable _innerException;
-
-    public CompoundException(@Nullable String msg)
-    {
-        this(msg, null);
-    }
-
-    public CompoundException(@Nullable Throwable exception)
-    {
-        this(null, exception);
-    }
-
-    public CompoundException(@Nullable String msg, @Nullable Throwable innerException)
-    {
-        super(msg);
-        _innerException = innerException;
-    }
-
-    @Nullable
-    public Throwable getInnerException()
-    {
-        return _innerException;
-    }
-
-    @Override
-    @NotNull
-    public String toString()
-    {
-        StringBuilder string = new StringBuilder();
-        string.append(super.toString());
-        if (_innerException != null) {
-            string.append("\n");
-            string.append("--- inner exception ---");
-            string.append("\n");
-            string.append(_innerException.toString());
-        }
-        return string.toString();
-    }
-
-    @Override
-    public void printStackTrace(@NotNull PrintStream s)
-    {
-        super.printStackTrace(s);
-        if (_innerException != null) {
-            s.println("--- inner exception ---");
-            _innerException.printStackTrace(s);
-        }
-    }
-
-    @Override
-    public void printStackTrace(@NotNull PrintWriter s)
-    {
-        super.printStackTrace(s);
-        if (_innerException != null) {
-            s.println("--- inner exception ---");
-            _innerException.printStackTrace(s);
-        }
-    }
-
-    @Override
-    public void printStackTrace()
-    {
-        super.printStackTrace();
-        if (_innerException != null) {
-            System.err.println("--- inner exception ---");
-            _innerException.printStackTrace();
-        }
-    }
-}
Index: unk/src/com/drew/lang/DateUtil.java
===================================================================
--- /trunk/src/com/drew/lang/DateUtil.java	(revision 16024)
+++ 	(revision )
@@ -1,32 +1,0 @@
-package com.drew.lang;
-
-/**
- * @author Drew Noakes http://drewnoakes.com
- */
-public class DateUtil
-{
-    private static int[] _daysInMonth365 = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-    public static boolean isValidDate(int year, int month, int day)
-    {
-        if (year < 1 || year > 9999 || month < 0 || month > 11)
-            return false;
-
-        int daysInMonth = _daysInMonth365[month];
-        if (month == 1)
-        {
-            boolean isLeapYear = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
-            if (isLeapYear)
-                daysInMonth++;
-        }
-
-        return day >= 1 && day <= daysInMonth;
-    }
-
-    public static boolean isValidTime(int hours, int minutes, int seconds)
-    {
-        return hours >= 0 && hours < 24
-            && minutes >= 0 && minutes < 60
-            && seconds >= 0 && seconds < 60;
-    }
-}
Index: unk/src/com/drew/lang/GeoLocation.java
===================================================================
--- /trunk/src/com/drew/lang/GeoLocation.java	(revision 16024)
+++ 	(revision )
@@ -1,163 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.text.DecimalFormat;
-
-/**
- * Represents a latitude and longitude pair, giving a position on earth in spherical coordinates.
- * <p>
- * Values of latitude and longitude are given in degrees.
- * <p>
- * This type is immutable.
- */
-public final class GeoLocation
-{
-    private final double _latitude;
-    private final double _longitude;
-
-    /**
-     * Instantiates a new instance of {@link GeoLocation}.
-     *
-     * @param latitude the latitude, in degrees
-     * @param longitude the longitude, in degrees
-     */
-    public GeoLocation(double latitude, double longitude)
-    {
-        _latitude = latitude;
-        _longitude = longitude;
-    }
-
-    /**
-     * @return the latitudinal angle of this location, in degrees.
-     */
-    public double getLatitude()
-    {
-        return _latitude;
-    }
-
-    /**
-     * @return the longitudinal angle of this location, in degrees.
-     */
-    public double getLongitude()
-    {
-        return _longitude;
-    }
-
-    /**
-     * @return true, if both latitude and longitude are equal to zero
-     */
-    public boolean isZero()
-    {
-        return _latitude == 0 && _longitude == 0;
-    }
-
-    /**
-     * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) representation as a string,
-     * of format: {@code -1° 23' 4.56"}
-     */
-    @NotNull
-    public static String decimalToDegreesMinutesSecondsString(double decimal)
-    {
-        double[] dms = decimalToDegreesMinutesSeconds(decimal);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return String.format("%s\u00B0 %s' %s\"", format.format(dms[0]), format.format(dms[1]), format.format(dms[2]));
-    }
-
-    /**
-     * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) component values, as
-     * a double array.
-     */
-    @NotNull
-    public static double[] decimalToDegreesMinutesSeconds(double decimal)
-    {
-        int d = (int)decimal;
-        double m = Math.abs((decimal % 1) * 60);
-        double s = (m % 1) * 60;
-        return new double[] { d, (int)m, s};
-    }
-
-    /**
-     * Converts DMS (degrees-minutes-seconds) rational values, as given in {@link com.drew.metadata.exif.GpsDirectory},
-     * into a single value in degrees, as a double.
-     */
-    @Nullable
-    public static Double degreesMinutesSecondsToDecimal(@NotNull final Rational degs, @NotNull final Rational mins, @NotNull final Rational secs, final boolean isNegative)
-    {
-        double decimal = Math.abs(degs.doubleValue())
-                + mins.doubleValue() / 60.0d
-                + secs.doubleValue() / 3600.0d;
-
-        if (Double.isNaN(decimal))
-            return null;
-
-        if (isNegative)
-            decimal *= -1;
-
-        return decimal;
-    }
-
-    @Override
-    public boolean equals(final Object o)
-    {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        GeoLocation that = (GeoLocation) o;
-        if (Double.compare(that._latitude, _latitude) != 0) return false;
-        if (Double.compare(that._longitude, _longitude) != 0) return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        int result;
-        long temp;
-        temp = _latitude != +0.0d ? Double.doubleToLongBits(_latitude) : 0L;
-        result = (int) (temp ^ (temp >>> 32));
-        temp = _longitude != +0.0d ? Double.doubleToLongBits(_longitude) : 0L;
-        result = 31 * result + (int) (temp ^ (temp >>> 32));
-        return result;
-    }
-
-    /**
-     * @return a string representation of this location, of format: {@code 1.23, 4.56}
-     */
-    @Override
-    @NotNull
-    public String toString()
-    {
-        return _latitude + ", " + _longitude;
-    }
-
-    /**
-     * @return a string representation of this location, of format: {@code -1° 23' 4.56", 54° 32' 1.92"}
-     */
-    @NotNull
-    public String toDMSString()
-    {
-        return decimalToDegreesMinutesSecondsString(_latitude) + ", " + decimalToDegreesMinutesSecondsString(_longitude);
-    }
-}
Index: unk/src/com/drew/lang/RandomAccessReader.java
===================================================================
--- /trunk/src/com/drew/lang/RandomAccessReader.java	(revision 16024)
+++ 	(revision )
@@ -1,444 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.StringValue;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-
-/**
- * Base class for random access data reading operations of common data types.
- * <p>
- * By default, the reader operates with Motorola byte order (big endianness).  This can be changed by calling
- * {@link com.drew.lang.RandomAccessReader#setMotorolaByteOrder(boolean)}.
- * <p>
- * Concrete implementations include:
- * <ul>
- *     <li>{@link ByteArrayReader}</li>
- * </ul>
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public abstract class RandomAccessReader
-{
-    private boolean _isMotorolaByteOrder = true;
-
-    public abstract int toUnshiftedOffset(int localOffset);
-
-    /**
-     * Gets the byte value at the specified byte <code>index</code>.
-     * <p>
-     * Implementations should not perform any bounds checking in this method. That should be performed
-     * in <code>validateIndex</code> and <code>isValidIndex</code>.
-     *
-     * @param index The index from which to read the byte
-     * @return The read byte value
-     * @throws IllegalArgumentException <code>index</code> is negative
-     * @throws BufferBoundsException if the requested byte is beyond the end of the underlying data source
-     * @throws IOException if the byte is unable to be read
-     */
-    public abstract byte getByte(int index) throws IOException;
-
-    /**
-     * Returns the required number of bytes from the specified index from the underlying source.
-     *
-     * @param index The index from which the bytes begins in the underlying source
-     * @param count The number of bytes to be returned
-     * @return The requested bytes
-     * @throws IllegalArgumentException <code>index</code> or <code>count</code> are negative
-     * @throws BufferBoundsException if the requested bytes extend beyond the end of the underlying data source
-     * @throws IOException if the byte is unable to be read
-     */
-    @NotNull
-    public abstract byte[] getBytes(int index, int count) throws IOException;
-
-    /**
-     * Ensures that the buffered bytes extend to cover the specified index. If not, an attempt is made
-     * to read to that point.
-     * <p>
-     * If the stream ends before the point is reached, a {@link BufferBoundsException} is raised.
-     *
-     * @param index the index from which the required bytes start
-     * @param bytesRequested the number of bytes which are required
-     * @throws IOException if the stream ends before the required number of bytes are acquired
-     */
-    protected abstract void validateIndex(int index, int bytesRequested) throws IOException;
-
-    protected abstract boolean isValidIndex(int index, int bytesRequested) throws IOException;
-
-    /**
-     * Returns the length of the data source in bytes.
-     * <p>
-     * This is a simple operation for implementations (such as
-     * {@link ByteArrayReader}) that have the entire data source available.
-     * <p>
-     * Users of this method must be aware that sequentially accessed implementations
-     * will have to read and buffer the entire data source in
-     * order to determine the length.
-     *
-     * @return the length of the data source, in bytes.
-     */
-    public abstract long getLength() throws IOException;
-
-    /**
-     * Sets the endianness of this reader.
-     * <ul>
-     * <li><code>true</code> for Motorola (or big) endianness (also known as network byte order), with MSB before LSB.</li>
-     * <li><code>false</code> for Intel (or little) endianness, with LSB before MSB.</li>
-     * </ul>
-     *
-     * @param motorolaByteOrder <code>true</code> for Motorola/big endian, <code>false</code> for Intel/little endian
-     */
-    public void setMotorolaByteOrder(boolean motorolaByteOrder)
-    {
-        _isMotorolaByteOrder = motorolaByteOrder;
-    }
-
-    /**
-     * Gets the endianness of this reader.
-     * <ul>
-     * <li><code>true</code> for Motorola (or big) endianness (also known as network byte order), with MSB before LSB.</li>
-     * <li><code>false</code> for Intel (or little) endianness, with LSB before MSB.</li>
-     * </ul>
-     */
-    public boolean isMotorolaByteOrder()
-    {
-        return _isMotorolaByteOrder;
-    }
-
-    /**
-     * Gets whether a bit at a specific index is set or not.
-     *
-     * @param index the number of bits at which to test
-     * @return true if the bit is set, otherwise false
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public boolean getBit(int index) throws IOException
-    {
-        int byteIndex = index / 8;
-        int bitIndex = index % 8;
-
-        validateIndex(byteIndex, 1);
-
-        byte b = getByte(byteIndex);
-        return ((b >> bitIndex) & 1) == 1;
-    }
-
-    /**
-     * Returns an unsigned 8-bit int calculated from one byte of data at the specified index.
-     *
-     * @param index position within the data buffer to read byte
-     * @return the 8 bit int value, between 0 and 255
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public short getUInt8(int index) throws IOException
-    {
-        validateIndex(index, 1);
-
-        return (short) (getByte(index) & 0xFF);
-    }
-
-    /**
-     * Returns a signed 8-bit int calculated from one byte of data at the specified index.
-     *
-     * @param index position within the data buffer to read byte
-     * @return the 8 bit int value, between 0x00 and 0xFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public byte getInt8(int index) throws IOException
-    {
-        validateIndex(index, 1);
-
-        return getByte(index);
-    }
-
-    /**
-     * Returns an unsigned 16-bit int calculated from two bytes of data at the specified index.
-     *
-     * @param index position within the data buffer to read first byte
-     * @return the 16 bit int value, between 0x0000 and 0xFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public int getUInt16(int index) throws IOException
-    {
-        validateIndex(index, 2);
-
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first
-            return (getByte(index    ) << 8 & 0xFF00) |
-                   (getByte(index + 1)      & 0xFF);
-        } else {
-            // Intel ordering - LSB first
-            return (getByte(index + 1) << 8 & 0xFF00) |
-                   (getByte(index    )      & 0xFF);
-        }
-    }
-
-    /**
-     * Returns a signed 16-bit int calculated from two bytes of data at the specified index (MSB, LSB).
-     *
-     * @param index position within the data buffer to read first byte
-     * @return the 16 bit int value, between 0x0000 and 0xFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public short getInt16(int index) throws IOException
-    {
-        validateIndex(index, 2);
-
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first
-            return (short) ((getByte(index    ) << 8 & (short)0xFF00) |
-                            (getByte(index + 1)      & (short)0xFF));
-        } else {
-            // Intel ordering - LSB first
-            return (short) ((getByte(index + 1) << 8 & (short)0xFF00) |
-                            (getByte(index    )      & (short)0xFF));
-        }
-    }
-
-    /**
-     * Get a 24-bit unsigned integer from the buffer, returning it as an int.
-     *
-     * @param index position within the data buffer to read first byte
-     * @return the unsigned 24-bit int value as a long, between 0x00000000 and 0x00FFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public int getInt24(int index) throws IOException
-    {
-        validateIndex(index, 3);
-
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first (big endian)
-            return ((getByte(index    )) << 16 & 0xFF0000) |
-                   ((getByte(index + 1)) << 8  & 0xFF00) |
-                   ((getByte(index + 2))       & 0xFF);
-        } else {
-            // Intel ordering - LSB first (little endian)
-            return ((getByte(index + 2)) << 16 & 0xFF0000) |
-                   ((getByte(index + 1)) << 8  & 0xFF00) |
-                   ((getByte(index    ))       & 0xFF);
-        }
-    }
-
-    /**
-     * Get a 32-bit unsigned integer from the buffer, returning it as a long.
-     *
-     * @param index position within the data buffer to read first byte
-     * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public long getUInt32(int index) throws IOException
-    {
-        validateIndex(index, 4);
-
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first (big endian)
-            return (((long)getByte(index    )) << 24 & 0xFF000000L) |
-                   (((long)getByte(index + 1)) << 16 & 0xFF0000L) |
-                   (((long)getByte(index + 2)) << 8  & 0xFF00L) |
-                   ((getByte(index + 3))       & 0xFFL);
-        } else {
-            // Intel ordering - LSB first (little endian)
-            return (((long)getByte(index + 3)) << 24 & 0xFF000000L) |
-                   (((long)getByte(index + 2)) << 16 & 0xFF0000L) |
-                   (((long)getByte(index + 1)) << 8  & 0xFF00L) |
-                   ((getByte(index    ))       & 0xFFL);
-        }
-    }
-
-    /**
-     * Returns a signed 32-bit integer from four bytes of data at the specified index the buffer.
-     *
-     * @param index position within the data buffer to read first byte
-     * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public int getInt32(int index) throws IOException
-    {
-        validateIndex(index, 4);
-
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first (big endian)
-            return (getByte(index    ) << 24 & 0xFF000000) |
-                   (getByte(index + 1) << 16 & 0xFF0000) |
-                   (getByte(index + 2) << 8  & 0xFF00) |
-                   (getByte(index + 3)       & 0xFF);
-        } else {
-            // Intel ordering - LSB first (little endian)
-            return (getByte(index + 3) << 24 & 0xFF000000) |
-                   (getByte(index + 2) << 16 & 0xFF0000) |
-                   (getByte(index + 1) << 8  & 0xFF00) |
-                   (getByte(index    )       & 0xFF);
-        }
-    }
-
-    /**
-     * Get a signed 64-bit integer from the buffer.
-     *
-     * @param index position within the data buffer to read first byte
-     * @return the 64 bit int value, between 0x0000000000000000 and 0xFFFFFFFFFFFFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public long getInt64(int index) throws IOException
-    {
-        validateIndex(index, 8);
-
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first
-            return ((long)getByte(index    ) << 56 & 0xFF00000000000000L) |
-                   ((long)getByte(index + 1) << 48 & 0xFF000000000000L) |
-                   ((long)getByte(index + 2) << 40 & 0xFF0000000000L) |
-                   ((long)getByte(index + 3) << 32 & 0xFF00000000L) |
-                   ((long)getByte(index + 4) << 24 & 0xFF000000L) |
-                   ((long)getByte(index + 5) << 16 & 0xFF0000L) |
-                   ((long)getByte(index + 6) << 8  & 0xFF00L) |
-                   (getByte(index + 7)       & 0xFFL);
-        } else {
-            // Intel ordering - LSB first
-            return ((long)getByte(index + 7) << 56 & 0xFF00000000000000L) |
-                   ((long)getByte(index + 6) << 48 & 0xFF000000000000L) |
-                   ((long)getByte(index + 5) << 40 & 0xFF0000000000L) |
-                   ((long)getByte(index + 4) << 32 & 0xFF00000000L) |
-                   ((long)getByte(index + 3) << 24 & 0xFF000000L) |
-                   ((long)getByte(index + 2) << 16 & 0xFF0000L) |
-                   ((long)getByte(index + 1) << 8  & 0xFF00L) |
-                   (getByte(index    )       & 0xFFL);
-        }
-    }
-
-    /**
-     * Gets a s15.16 fixed point float from the buffer.
-     * <p>
-     * This particular fixed point encoding has one sign bit, 15 numerator bits and 16 denominator bits.
-     *
-     * @return the floating point value
-     * @throws IOException the buffer does not contain enough bytes to service the request, or index is negative
-     */
-    public float getS15Fixed16(int index) throws IOException
-    {
-        validateIndex(index, 4);
-
-        if (_isMotorolaByteOrder) {
-            float res = (getByte(index    ) & 0xFF) << 8 |
-                        (getByte(index + 1) & 0xFF);
-            int d =     (getByte(index + 2) & 0xFF) << 8 |
-                        (getByte(index + 3) & 0xFF);
-            return (float)(res + d/65536.0);
-        } else {
-            // this particular branch is untested
-            float res = (getByte(index + 3) & 0xFF) << 8 |
-                        (getByte(index + 2) & 0xFF);
-            int d =     (getByte(index + 1) & 0xFF) << 8 |
-                        (getByte(index    ) & 0xFF);
-            return (float)(res + d/65536.0);
-        }
-    }
-
-    public float getFloat32(int index) throws IOException
-    {
-        return Float.intBitsToFloat(getInt32(index));
-    }
-
-    public double getDouble64(int index) throws IOException
-    {
-        return Double.longBitsToDouble(getInt64(index));
-    }
-
-    @NotNull
-    public StringValue getStringValue(int index, int bytesRequested, @Nullable Charset charset) throws IOException
-    {
-        return new StringValue(getBytes(index, bytesRequested), charset);
-    }
-
-    @NotNull
-    public String getString(int index, int bytesRequested, @NotNull Charset charset) throws IOException
-    {
-        return new String(getBytes(index, bytesRequested), charset.name());
-    }
-
-    @NotNull
-    public String getString(int index, int bytesRequested, @NotNull String charset) throws IOException
-    {
-        byte[] bytes = getBytes(index, bytesRequested);
-        try {
-            return new String(bytes, charset);
-        } catch (UnsupportedEncodingException e) {
-            return new String(bytes);
-        }
-    }
-
-    /**
-     * Creates a String from the _data buffer starting at the specified index,
-     * and ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>.
-     *
-     * @param index          The index within the buffer at which to start reading the string.
-     * @param maxLengthBytes The maximum number of bytes to read.  If a zero-byte is not reached within this limit,
-     *                       reading will stop and the string will be truncated to this length.
-     * @return The read string.
-     * @throws IOException The buffer does not contain enough bytes to satisfy this request.
-     */
-    @NotNull
-    public String getNullTerminatedString(int index, int maxLengthBytes, @NotNull Charset charset) throws IOException
-    {
-        return new String(getNullTerminatedBytes(index, maxLengthBytes), charset.name());
-    }
-
-    @NotNull
-    public StringValue getNullTerminatedStringValue(int index, int maxLengthBytes, @Nullable Charset charset) throws IOException
-    {
-        byte[] bytes = getNullTerminatedBytes(index, maxLengthBytes);
-
-        return new StringValue(bytes, charset);
-    }
-
-    /**
-     * Returns the sequence of bytes punctuated by a <code>\0</code> value.
-     *
-     * @param index The index within the buffer at which to start reading the string.
-     * @param maxLengthBytes The maximum number of bytes to read. If a <code>\0</code> byte is not reached within this limit,
-     * the returned array will be <code>maxLengthBytes</code> long.
-     * @return The read byte array, excluding the null terminator.
-     * @throws IOException The buffer does not contain enough bytes to satisfy this request.
-     */
-    @NotNull
-    public byte[] getNullTerminatedBytes(int index, int maxLengthBytes) throws IOException
-    {
-        byte[] buffer = getBytes(index, maxLengthBytes);
-
-        // Count the number of non-null bytes
-        int length = 0;
-        while (length < buffer.length && buffer[length] != 0)
-            length++;
-
-        if (length == maxLengthBytes)
-            return buffer;
-
-        byte[] bytes = new byte[length];
-        if (length > 0)
-            System.arraycopy(buffer, 0, bytes, 0, length);
-        return bytes;
-    }
-}
Index: unk/src/com/drew/lang/Rational.java
===================================================================
--- /trunk/src/com/drew/lang/Rational.java	(revision 16024)
+++ 	(revision )
@@ -1,323 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.io.Serializable;
-
-/**
- * Immutable class for holding a rational number without loss of precision.  Provides
- * a familiar representation via {@link Rational#toString} in form <code>numerator/denominator</code>.
- *
- * Note that any value with a numerator of zero will be treated as zero, even if the
- * denominator is also zero.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class Rational extends java.lang.Number implements Comparable<Rational>, Serializable
-{
-    private static final long serialVersionUID = 510688928138848770L;
-
-    /** Holds the numerator. */
-    private final long _numerator;
-
-    /** Holds the denominator. */
-    private final long _denominator;
-
-    /**
-     * Creates a new instance of Rational.  Rational objects are immutable, so
-     * once you've set your numerator and denominator values here, you're stuck
-     * with them!
-     */
-    public Rational(long numerator, long denominator)
-    {
-        _numerator = numerator;
-        _denominator = denominator;
-    }
-
-    /**
-     * Returns the value of the specified number as a <code>double</code>.
-     * This may involve rounding.
-     *
-     * @return the numeric value represented by this object after conversion
-     *         to type <code>double</code>.
-     */
-    @Override
-    public double doubleValue()
-    {
-        return _numerator == 0
-            ? 0.0
-            : (double) _numerator / (double) _denominator;
-    }
-
-    /**
-     * Returns the value of the specified number as a <code>float</code>.
-     * This may involve rounding.
-     *
-     * @return the numeric value represented by this object after conversion
-     *         to type <code>float</code>.
-     */
-    @Override
-    public float floatValue()
-    {
-        return _numerator == 0
-            ? 0.0f
-            : (float) _numerator / (float) _denominator;
-    }
-
-    /**
-     * Returns the value of the specified number as a <code>byte</code>.
-     * This may involve rounding or truncation.  This implementation simply
-     * casts the result of {@link Rational#doubleValue} to <code>byte</code>.
-     *
-     * @return the numeric value represented by this object after conversion
-     *         to type <code>byte</code>.
-     */
-    @Override
-    public final byte byteValue()
-    {
-        return (byte) doubleValue();
-    }
-
-    /**
-     * Returns the value of the specified number as an <code>int</code>.
-     * This may involve rounding or truncation.  This implementation simply
-     * casts the result of {@link Rational#doubleValue} to <code>int</code>.
-     *
-     * @return the numeric value represented by this object after conversion
-     *         to type <code>int</code>.
-     */
-    @Override
-    public final int intValue()
-    {
-        return (int) doubleValue();
-    }
-
-    /**
-     * Returns the value of the specified number as a <code>long</code>.
-     * This may involve rounding or truncation.  This implementation simply
-     * casts the result of {@link Rational#doubleValue} to <code>long</code>.
-     *
-     * @return the numeric value represented by this object after conversion
-     *         to type <code>long</code>.
-     */
-    @Override
-    public final long longValue()
-    {
-        return (long) doubleValue();
-    }
-
-    /**
-     * Returns the value of the specified number as a <code>short</code>.
-     * This may involve rounding or truncation.  This implementation simply
-     * casts the result of {@link Rational#doubleValue} to <code>short</code>.
-     *
-     * @return the numeric value represented by this object after conversion
-     *         to type <code>short</code>.
-     */
-    @Override
-    public final short shortValue()
-    {
-        return (short) doubleValue();
-    }
-
-
-    /** Returns the denominator. */
-    public final long getDenominator()
-    {
-        return this._denominator;
-    }
-
-    /** Returns the numerator. */
-    public final long getNumerator()
-    {
-        return this._numerator;
-    }
-
-    /**
-     * Returns the reciprocal value of this object as a new Rational.
-     *
-     * @return the reciprocal in a new object
-     */
-    @NotNull
-    public Rational getReciprocal()
-    {
-        return new Rational(this._denominator, this._numerator);
-    }
-
-    /** Checks if this {@link Rational} number is an Integer, either positive or negative. */
-    public boolean isInteger()
-    {
-        return _denominator == 1 ||
-                (_denominator != 0 && (_numerator % _denominator == 0)) ||
-                (_denominator == 0 && _numerator == 0);
-    }
-
-    /** Checks if either the numerator or denominator are zero. */
-    public boolean isZero()
-    {
-        return _numerator == 0 || _denominator == 0;
-    }
-
-    /**
-     * Returns a string representation of the object of form <code>numerator/denominator</code>.
-     *
-     * @return a string representation of the object.
-     */
-    @Override
-    @NotNull
-    public String toString()
-    {
-        return _numerator + "/" + _denominator;
-    }
-
-    /** Returns the simplest representation of this {@link Rational}'s value possible. */
-    @NotNull
-    public String toSimpleString(boolean allowDecimal)
-    {
-        if (_denominator == 0 && _numerator != 0) {
-            return toString();
-        } else if (isInteger()) {
-            return Integer.toString(intValue());
-        } else if (_numerator != 1 && _denominator % _numerator == 0) {
-            // common factor between denominator and numerator
-            long newDenominator = _denominator / _numerator;
-            return new Rational(1, newDenominator).toSimpleString(allowDecimal);
-        } else {
-            Rational simplifiedInstance = getSimplifiedInstance();
-            if (allowDecimal) {
-                String doubleString = Double.toString(simplifiedInstance.doubleValue());
-                if (doubleString.length() < 5) {
-                    return doubleString;
-                }
-            }
-            return simplifiedInstance.toString();
-        }
-    }
-
-    /**
-     * Compares two {@link Rational} instances, returning true if they are mathematically
-     * equivalent (in consistence with {@link Rational#equals(Object)} method).
-     *
-     * @param that the {@link Rational} to compare this instance to.
-     * @return the value {@code 0} if this {@link Rational} is
-     *         equal to the argument {@link Rational} mathematically; a value less
-     *         than {@code 0} if this {@link Rational} is less
-     *         than the argument {@link Rational}; and a value greater
-     *         than {@code 0} if this {@link Rational} is greater than the argument
-     *         {@link Rational}.
-     */
-    public int compareTo(@NotNull Rational that) {
-        return Double.compare(this.doubleValue(), that.doubleValue());
-    }
-
-    /**
-     * Indicates whether this instance and <code>other</code> are numerically equal,
-     * even if their representations differ.
-     *
-     * For example, 1/2 is equal to 10/20 by this method.
-     * Similarly, 1/0 is equal to 100/0 by this method.
-     * To test equal representations, use EqualsExact.
-     *
-     * @param other The rational value to compare with
-     */
-    public boolean equals(Rational other) {
-        return other.doubleValue() == doubleValue();
-    }
-
-    /**
-     * Indicates whether this instance and <code>other</code> have identical
-     * Numerator and Denominator.
-     * <p>
-     * For example, 1/2 is not equal to 10/20 by this method.
-     * Similarly, 1/0 is not equal to 100/0 by this method.
-     * To test numerically equivalence, use Equals(Rational).</p>
-     *
-     * @param other The rational value to compare with
-     */
-    public boolean equalsExact(Rational other) {
-        return getDenominator() == other.getDenominator() && getNumerator() == other.getNumerator();
-    }
-
-    /**
-     * Compares two {@link Rational} instances, returning true if they are mathematically
-     * equivalent.
-     *
-     * @param obj the {@link Rational} to compare this instance to.
-     * @return true if instances are mathematically equivalent, otherwise false.  Will also
-     *         return false if <code>obj</code> is not an instance of {@link Rational}.
-     */
-    @Override
-    public boolean equals(@Nullable Object obj)
-    {
-        if (obj==null || !(obj instanceof Rational))
-            return false;
-        Rational that = (Rational) obj;
-        return this.doubleValue() == that.doubleValue();
-    }
-
-    @Override
-    public int hashCode()
-    {
-        return (23 * (int)_denominator) + (int)_numerator;
-    }
-
-    /**
-     * <p>
-     * Simplifies the representation of this {@link Rational} number.</p>
-     * <p>
-     * For example, 5/10 simplifies to 1/2 because both Numerator
-     * and Denominator share a common factor of 5.</p>
-     * <p>
-     * Uses the Euclidean Algorithm to find the greatest common divisor.</p>
-     *
-     * @return A simplified instance if one exists, otherwise a copy of the original value.
-     */
-    @NotNull
-    public Rational getSimplifiedInstance()
-    {
-        long gcd = GCD(_numerator, _denominator);
-
-        return new Rational(_numerator / gcd, _denominator / gcd);
-    }
-
-    private static long GCD(long a, long b)
-    {
-        if (a < 0)
-            a = -a;
-        if (b < 0)
-            b = -b;
-
-        while (a != 0 && b != 0)
-        {
-            if (a > b)
-                a %= b;
-            else
-                b %= a;
-        }
-
-        return a == 0 ? b : a;
-    }
-}
Index: unk/src/com/drew/lang/SequentialByteArrayReader.java
===================================================================
--- /trunk/src/com/drew/lang/SequentialByteArrayReader.java	(revision 16024)
+++ 	(revision )
@@ -1,130 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-/**
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class SequentialByteArrayReader extends SequentialReader
-{
-    @NotNull
-    private final byte[] _bytes;
-    private int _index;
-
-    @Override
-    public long getPosition()
-    {
-        return _index;
-    }
-
-    public SequentialByteArrayReader(@NotNull byte[] bytes)
-    {
-        this(bytes, 0);
-    }
-
-    @SuppressWarnings("ConstantConditions")
-    public SequentialByteArrayReader(@NotNull byte[] bytes, int baseIndex)
-    {
-        if (bytes == null)
-            throw new NullPointerException();
-
-        _bytes = bytes;
-        _index = baseIndex;
-    }
-
-    @Override
-    public byte getByte() throws IOException
-    {
-        if (_index >= _bytes.length) {
-            throw new EOFException("End of data reached.");
-        }
-        return _bytes[_index++];
-    }
-
-    @NotNull
-    @Override
-    public byte[] getBytes(int count) throws IOException
-    {
-        if (_index + count > _bytes.length) {
-            throw new EOFException("End of data reached.");
-        }
-
-        byte[] bytes = new byte[count];
-        System.arraycopy(_bytes, _index, bytes, 0, count);
-        _index += count;
-
-        return bytes;
-    }
-
-    @Override
-    public void getBytes(@NotNull byte[] buffer, int offset, int count) throws IOException
-    {
-        if (_index + count > _bytes.length) {
-            throw new EOFException("End of data reached.");
-        }
-
-        System.arraycopy(_bytes, _index, buffer, offset, count);
-        _index += count;
-    }
-
-    @Override
-    public void skip(long n) throws IOException
-    {
-        if (n < 0) {
-            throw new IllegalArgumentException("n must be zero or greater.");
-        }
-
-        if (_index + n > _bytes.length) {
-            throw new EOFException("End of data reached.");
-        }
-
-        _index += n;
-    }
-
-    @Override
-    public boolean trySkip(long n) throws IOException
-    {
-        if (n < 0) {
-            throw new IllegalArgumentException("n must be zero or greater.");
-        }
-
-        _index += n;
-
-        if (_index > _bytes.length) {
-            _index = _bytes.length;
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int available() {
-        return _bytes.length - _index;
-    }
-}
Index: unk/src/com/drew/lang/SequentialReader.java
===================================================================
--- /trunk/src/com/drew/lang/SequentialReader.java	(revision 16024)
+++ 	(revision )
@@ -1,389 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.StringValue;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public abstract class SequentialReader
-{
-    // TODO review whether the masks are needed (in both this and RandomAccessReader)
-
-    private boolean _isMotorolaByteOrder = true;
-
-    public abstract long getPosition() throws IOException;
-
-    /**
-     * Gets the next byte in the sequence.
-     *
-     * @return The read byte value
-     */
-    public abstract byte getByte() throws IOException;
-
-    /**
-     * Returns the required number of bytes from the sequence.
-     *
-     * @param count The number of bytes to be returned
-     * @return The requested bytes
-     */
-    @NotNull
-    public abstract byte[] getBytes(int count) throws IOException;
-
-    /**
-     * Retrieves bytes, writing them into a caller-provided buffer.
-     * @param buffer The array to write bytes to.
-     * @param offset The starting position within buffer to write to.
-     * @param count The number of bytes to be written.
-     */
-    public abstract void getBytes(@NotNull byte[] buffer, int offset, int count) throws IOException;
-
-    /**
-     * Skips forward in the sequence. If the sequence ends, an {@link EOFException} is thrown.
-     *
-     * @param n the number of byte to skip. Must be zero or greater.
-     * @throws EOFException the end of the sequence is reached.
-     * @throws IOException an error occurred reading from the underlying source.
-     */
-    public abstract void skip(long n) throws IOException;
-
-    /**
-     * Skips forward in the sequence, returning a boolean indicating whether the skip succeeded, or whether the sequence ended.
-     *
-     * @param n the number of byte to skip. Must be zero or greater.
-     * @return a boolean indicating whether the skip succeeded, or whether the sequence ended.
-     * @throws IOException an error occurred reading from the underlying source.
-     */
-    public abstract boolean trySkip(long n) throws IOException;
-
-    /**
-     * Returns an estimate of the number of bytes that can be read (or skipped
-     * over) from this {@link SequentialReader} without blocking by the next
-     * invocation of a method for this input stream. A single read or skip of
-     * this many bytes will not block, but may read or skip fewer bytes.
-     * <p>
-     * Note that while some implementations of {@link SequentialReader} like
-     * {@link SequentialByteArrayReader} will return the total remaining number
-     * of bytes in the stream, others will not. It is never correct to use the
-     * return value of this method to allocate a buffer intended to hold all
-     * data in this stream.
-     *
-     * @return an estimate of the number of bytes that can be read (or skipped
-     *         over) from this {@link SequentialReader} without blocking or
-     *         {@code 0} when it reaches the end of the input stream.
-     */
-    public abstract int available();
-
-    /**
-     * Sets the endianness of this reader.
-     * <ul>
-     * <li><code>true</code> for Motorola (or big) endianness (also known as network byte order), with MSB before LSB.</li>
-     * <li><code>false</code> for Intel (or little) endianness, with LSB before MSB.</li>
-     * </ul>
-     *
-     * @param motorolaByteOrder <code>true</code> for Motorola/big endian, <code>false</code> for Intel/little endian
-     */
-    public void setMotorolaByteOrder(boolean motorolaByteOrder)
-    {
-        _isMotorolaByteOrder = motorolaByteOrder;
-    }
-
-    /**
-     * Gets the endianness of this reader.
-     * <ul>
-     * <li><code>true</code> for Motorola (or big) endianness (also known as network byte order), with MSB before LSB.</li>
-     * <li><code>false</code> for Intel (or little) endianness, with LSB before MSB.</li>
-     * </ul>
-     */
-    public boolean isMotorolaByteOrder()
-    {
-        return _isMotorolaByteOrder;
-    }
-
-    /**
-     * Returns an unsigned 8-bit int calculated from the next byte of the sequence.
-     *
-     * @return the 8 bit int value, between 0 and 255
-     */
-    public short getUInt8() throws IOException
-    {
-        return (short) (getByte() & 0xFF);
-    }
-
-    /**
-     * Returns a signed 8-bit int calculated from the next byte the sequence.
-     *
-     * @return the 8 bit int value, between 0x00 and 0xFF
-     */
-    public byte getInt8() throws IOException
-    {
-        return getByte();
-    }
-
-    /**
-     * Returns an unsigned 16-bit int calculated from the next two bytes of the sequence.
-     *
-     * @return the 16 bit int value, between 0x0000 and 0xFFFF
-     */
-    public int getUInt16() throws IOException
-    {
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first
-            return (getByte() << 8 & 0xFF00) |
-                   (getByte()      & 0xFF);
-        } else {
-            // Intel ordering - LSB first
-            return (getByte()      & 0xFF) |
-                   (getByte() << 8 & 0xFF00);
-        }
-    }
-
-    /**
-     * Returns a signed 16-bit int calculated from two bytes of data (MSB, LSB).
-     *
-     * @return the 16 bit int value, between 0x0000 and 0xFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request
-     */
-    public short getInt16() throws IOException
-    {
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first
-            return (short) (((short)getByte() << 8 & (short)0xFF00) |
-                            ((short)getByte()      & (short)0xFF));
-        } else {
-            // Intel ordering - LSB first
-            return (short) (((short)getByte()      & (short)0xFF) |
-                            ((short)getByte() << 8 & (short)0xFF00));
-        }
-    }
-
-    /**
-     * Get a 32-bit unsigned integer from the buffer, returning it as a long.
-     *
-     * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request
-     */
-    public long getUInt32() throws IOException
-    {
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first (big endian)
-            return (((long)getByte()) << 24 & 0xFF000000L) |
-                   (((long)getByte()) << 16 & 0xFF0000L) |
-                   (((long)getByte()) << 8  & 0xFF00L) |
-                   (((long)getByte())       & 0xFFL);
-        } else {
-            // Intel ordering - LSB first (little endian)
-            return (((long)getByte())       & 0xFFL) |
-                   (((long)getByte()) << 8  & 0xFF00L) |
-                   (((long)getByte()) << 16 & 0xFF0000L) |
-                   (((long)getByte()) << 24 & 0xFF000000L);
-        }
-    }
-
-    /**
-     * Returns a signed 32-bit integer from four bytes of data.
-     *
-     * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request
-     */
-    public int getInt32() throws IOException
-    {
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first (big endian)
-            return (getByte() << 24 & 0xFF000000) |
-                   (getByte() << 16 & 0xFF0000) |
-                   (getByte() << 8  & 0xFF00) |
-                   (getByte()       & 0xFF);
-        } else {
-            // Intel ordering - LSB first (little endian)
-            return (getByte()       & 0xFF) |
-                   (getByte() << 8  & 0xFF00) |
-                   (getByte() << 16 & 0xFF0000) |
-                   (getByte() << 24 & 0xFF000000);
-        }
-    }
-
-    /**
-     * Get a signed 64-bit integer from the buffer.
-     *
-     * @return the 64 bit int value, between 0x0000000000000000 and 0xFFFFFFFFFFFFFFFF
-     * @throws IOException the buffer does not contain enough bytes to service the request
-     */
-    public long getInt64() throws IOException
-    {
-        if (_isMotorolaByteOrder) {
-            // Motorola - MSB first
-            return ((long)getByte() << 56 & 0xFF00000000000000L) |
-                   ((long)getByte() << 48 & 0xFF000000000000L) |
-                   ((long)getByte() << 40 & 0xFF0000000000L) |
-                   ((long)getByte() << 32 & 0xFF00000000L) |
-                   ((long)getByte() << 24 & 0xFF000000L) |
-                   ((long)getByte() << 16 & 0xFF0000L) |
-                   ((long)getByte() << 8  & 0xFF00L) |
-                   ((long)getByte()       & 0xFFL);
-        } else {
-            // Intel ordering - LSB first
-            return ((long)getByte()       & 0xFFL) |
-                   ((long)getByte() << 8  & 0xFF00L) |
-                   ((long)getByte() << 16 & 0xFF0000L) |
-                   ((long)getByte() << 24 & 0xFF000000L) |
-                   ((long)getByte() << 32 & 0xFF00000000L) |
-                   ((long)getByte() << 40 & 0xFF0000000000L) |
-                   ((long)getByte() << 48 & 0xFF000000000000L) |
-                   ((long)getByte() << 56 & 0xFF00000000000000L);
-        }
-    }
-
-    /**
-     * Gets a s15.16 fixed point float from the buffer.
-     * <p>
-     * This particular fixed point encoding has one sign bit, 15 numerator bits and 16 denominator bits.
-     *
-     * @return the floating point value
-     * @throws IOException the buffer does not contain enough bytes to service the request
-     */
-    public float getS15Fixed16() throws IOException
-    {
-        if (_isMotorolaByteOrder) {
-            float res = (getByte() & 0xFF) << 8 |
-                        (getByte() & 0xFF);
-            int d =     (getByte() & 0xFF) << 8 |
-                        (getByte() & 0xFF);
-            return (float)(res + d/65536.0);
-        } else {
-            // this particular branch is untested
-            int d =     (getByte() & 0xFF) |
-                        (getByte() & 0xFF) << 8;
-            float res = (getByte() & 0xFF) |
-                        (getByte() & 0xFF) << 8;
-            return (float)(res + d/65536.0);
-        }
-    }
-
-    public float getFloat32() throws IOException
-    {
-        return Float.intBitsToFloat(getInt32());
-    }
-
-    public double getDouble64() throws IOException
-    {
-        return Double.longBitsToDouble(getInt64());
-    }
-
-    @NotNull
-    public String getString(int bytesRequested) throws IOException
-    {
-        return new String(getBytes(bytesRequested));
-    }
-
-    @NotNull
-    public String getString(int bytesRequested, String charset) throws IOException
-    {
-        byte[] bytes = getBytes(bytesRequested);
-        try {
-            return new String(bytes, charset);
-        } catch (UnsupportedEncodingException e) {
-            return new String(bytes);
-        }
-    }
-
-    @NotNull
-    public String getString(int bytesRequested, @NotNull Charset charset) throws IOException
-    {
-        byte[] bytes = getBytes(bytesRequested);
-        return new String(bytes, charset);
-    }
-
-    @NotNull
-    public StringValue getStringValue(int bytesRequested, @Nullable Charset charset) throws IOException
-    {
-        return new StringValue(getBytes(bytesRequested), charset);
-    }
-
-    /**
-     * Creates a String from the stream, ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>.
-     *
-     * @param maxLengthBytes The maximum number of bytes to read.  If a zero-byte is not reached within this limit,
-     *                       reading will stop and the string will be truncated to this length.
-     * @return The read string.
-     * @throws IOException The buffer does not contain enough bytes to satisfy this request.
-     */
-    @NotNull
-    public String getNullTerminatedString(int maxLengthBytes, Charset charset) throws IOException
-    {
-       return getNullTerminatedStringValue(maxLengthBytes, charset).toString();
-    }
-
-    /**
-     * Creates a String from the stream, ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>.
-     *
-     * @param maxLengthBytes The maximum number of bytes to read.  If a <code>\0</code> byte is not reached within this limit,
-     *                       reading will stop and the string will be truncated to this length.
-     * @param charset The <code>Charset</code> to register with the returned <code>StringValue</code>, or <code>null</code> if the encoding
-     *                is unknown
-     * @return The read string.
-     * @throws IOException The buffer does not contain enough bytes to satisfy this request.
-     */
-    @NotNull
-    public StringValue getNullTerminatedStringValue(int maxLengthBytes, Charset charset) throws IOException
-    {
-        byte[] bytes = getNullTerminatedBytes(maxLengthBytes);
-
-        return new StringValue(bytes, charset);
-    }
-
-    /**
-     * Returns the sequence of bytes punctuated by a <code>\0</code> value.
-     *
-     * @param maxLengthBytes The maximum number of bytes to read. If a <code>\0</code> byte is not reached within this limit,
-     * the returned array will be <code>maxLengthBytes</code> long.
-     * @return The read byte array, excluding the null terminator.
-     * @throws IOException The buffer does not contain enough bytes to satisfy this request.
-     */
-    @NotNull
-    public byte[] getNullTerminatedBytes(int maxLengthBytes) throws IOException
-    {
-        byte[] buffer = new byte[maxLengthBytes];
-
-        // Count the number of non-null bytes
-        int length = 0;
-        while (length < buffer.length && (buffer[length] = getByte()) != 0)
-            length++;
-
-        if (length == maxLengthBytes)
-            return buffer;
-
-        byte[] bytes = new byte[length];
-        if (length > 0)
-            System.arraycopy(buffer, 0, bytes, 0, length);
-        return bytes;
-    }
-}
Index: unk/src/com/drew/lang/StreamReader.java
===================================================================
--- /trunk/src/com/drew/lang/StreamReader.java	(revision 16024)
+++ 	(revision )
@@ -1,140 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class StreamReader extends SequentialReader
-{
-    @NotNull
-    private final InputStream _stream;
-
-    private long _pos;
-
-    @Override
-    public long getPosition()
-    {
-        return _pos;
-    }
-
-    @SuppressWarnings("ConstantConditions")
-    public StreamReader(@NotNull InputStream stream)
-    {
-        if (stream == null)
-            throw new NullPointerException();
-
-        _stream = stream;
-        _pos = 0;
-    }
-
-    @Override
-    public byte getByte() throws IOException
-    {
-        int value = _stream.read();
-        if (value == -1)
-            throw new EOFException("End of data reached.");
-        _pos++;
-        return (byte)value;
-    }
-
-    @NotNull
-    @Override
-    public byte[] getBytes(int count) throws IOException
-    {
-        byte[] bytes = new byte[count];
-        getBytes(bytes, 0, count);
-        return bytes;
-    }
-
-    @Override
-    public void getBytes(@NotNull byte[] buffer, int offset, int count) throws IOException
-    {
-        int totalBytesRead = 0;
-        while (totalBytesRead != count)
-        {
-            final int bytesRead = _stream.read(buffer, offset + totalBytesRead, count - totalBytesRead);
-            if (bytesRead == -1)
-                throw new EOFException("End of data reached.");
-            totalBytesRead += bytesRead;
-            assert(totalBytesRead <= count);
-        }
-        _pos += totalBytesRead;
-    }
-
-    @Override
-    public void skip(long n) throws IOException
-    {
-        if (n < 0)
-            throw new IllegalArgumentException("n must be zero or greater.");
-
-        long skippedCount = skipInternal(n);
-
-        if (skippedCount != n)
-            throw new EOFException(String.format("Unable to skip. Requested %d bytes but skipped %d.", n, skippedCount));
-    }
-
-    @Override
-    public boolean trySkip(long n) throws IOException
-    {
-        if (n < 0)
-            throw new IllegalArgumentException("n must be zero or greater.");
-
-        return skipInternal(n) == n;
-    }
-
-    @Override
-    public int available() {
-        try {
-            return _stream.available();
-        } catch (IOException e) {
-            return 0;
-        }
-    }
-
-    private long skipInternal(long n) throws IOException
-    {
-        // It seems that for some streams, such as BufferedInputStream, that skip can return
-        // some smaller number than was requested. So loop until we either skip enough, or
-        // InputStream.skip returns zero.
-        //
-        // See http://stackoverflow.com/questions/14057720/robust-skipping-of-data-in-a-java-io-inputstream-and-its-subtypes
-        //
-        long skippedTotal = 0;
-        while (skippedTotal != n) {
-            long skipped = _stream.skip(n - skippedTotal);
-            assert(skipped >= 0);
-            skippedTotal += skipped;
-            if (skipped == 0)
-                break;
-        }
-        _pos += skippedTotal;
-        return skippedTotal;
-    }
-}
Index: unk/src/com/drew/lang/StringUtil.java
===================================================================
--- /trunk/src/com/drew/lang/StringUtil.java	(revision 16024)
+++ 	(revision )
@@ -1,115 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Iterator;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-public final class StringUtil
-{
-    @NotNull
-    public static String join(@NotNull Iterable<? extends CharSequence> strings, @NotNull String delimiter)
-    {
-        int capacity = 0;
-        int delimLength = delimiter.length();
-
-        Iterator<? extends CharSequence> iter = strings.iterator();
-        if (iter.hasNext())
-            capacity += iter.next().length() + delimLength;
-
-        StringBuilder buffer = new StringBuilder(capacity);
-        iter = strings.iterator();
-        if (iter.hasNext()) {
-            buffer.append(iter.next());
-            while (iter.hasNext()) {
-                buffer.append(delimiter);
-                buffer.append(iter.next());
-            }
-        }
-        return buffer.toString();
-    }
-
-    @NotNull
-    public static <T extends CharSequence> String join(@NotNull T[] strings, @NotNull String delimiter)
-    {
-        int capacity = 0;
-        int delimLength = delimiter.length();
-        for (T value : strings)
-            capacity += value.length() + delimLength;
-
-        StringBuilder buffer = new StringBuilder(capacity);
-        boolean first = true;
-        for (T value : strings) {
-            if (!first) {
-                buffer.append(delimiter);
-            } else {
-                first = false;
-            }
-            buffer.append(value);
-        }
-        return buffer.toString();
-    }
-
-    @NotNull
-    public static String fromStream(@NotNull InputStream stream) throws IOException
-    {
-        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
-        StringBuilder sb = new StringBuilder();
-        String line;
-        while ((line = reader.readLine()) != null) {
-            sb.append(line);
-        }
-        return sb.toString();
-    }
-
-    public static int compare(@Nullable String s1, @Nullable String s2)
-    {
-        boolean null1 = s1 == null;
-        boolean null2 = s2 == null;
-
-        if (null1 && null2) {
-            return 0;
-        } else if (null1) {
-            return -1;
-        } else if (null2) {
-            return 1;
-        } else {
-            return s1.compareTo(s2);
-        }
-    }
-
-    @NotNull
-    public static String urlEncode(@NotNull String name)
-    {
-        // Sufficient for now, it seems
-        return name.replace(" ", "%20");
-    }
-}
Index: unk/src/com/drew/lang/annotations/NotNull.java
===================================================================
--- /trunk/src/com/drew/lang/annotations/NotNull.java	(revision 16024)
+++ 	(revision )
@@ -1,29 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang.annotations;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-public @interface NotNull
-{
-}
Index: unk/src/com/drew/lang/annotations/Nullable.java
===================================================================
--- /trunk/src/com/drew/lang/annotations/Nullable.java	(revision 16024)
+++ 	(revision )
@@ -1,29 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang.annotations;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-public @interface Nullable
-{
-}
Index: unk/src/com/drew/lang/annotations/SuppressWarnings.java
===================================================================
--- /trunk/src/com/drew/lang/annotations/SuppressWarnings.java	(revision 16024)
+++ 	(revision )
@@ -1,42 +1,0 @@
-/*
- * Copyright 2002-2011 Andreas Ziermann
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.lang.annotations;
-
-/**
- * Used to suppress specific code analysis warnings produced by the Findbugs tool.
- *
- * @author Andreas Ziermann
- */
-public @interface SuppressWarnings
-{
-    /**
-     * The name of the warning to be suppressed.
-     * @return The name of the warning to be suppressed.
-     */
-    @NotNull String value();
-
-    /**
-     * An explanation of why it is valid to suppress the warning in a particular situation/context.
-     * @return An explanation of why it is valid to suppress the warning in a particular situation/context.
-     */
-    @NotNull String justification();
-}
Index: unk/src/com/drew/lang/annotations/package-info.java
===================================================================
--- /trunk/src/com/drew/lang/annotations/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,5 +1,0 @@
-/**
- * Contains annotations used to extend the signatures of methods and fields, allowing tools such as IntelliJ IDEA
- * to provide design-time warnings about potential run-time errors.
- */
-package com.drew.lang.annotations;
Index: unk/src/com/drew/lang/package-info.java
===================================================================
--- /trunk/src/com/drew/lang/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes of generic utility.
- */
-package com.drew.lang;
Index: unk/src/com/drew/metadata/Age.java
===================================================================
--- /trunk/src/com/drew/metadata/Age.java	(revision 16024)
+++ 	(revision )
@@ -1,170 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * Represents an age in years, months, days, hours, minutes and seconds.
- * <p>
- * Used by certain Panasonic cameras which have face recognition features.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class Age
-{
-    private final int _years;
-    private final int _months;
-    private final int _days;
-    private final int _hours;
-    private final int _minutes;
-    private final int _seconds;
-
-    /**
-     * Parses an age object from the string format used by Panasonic cameras:
-     * <code>0031:07:15 00:00:00</code>
-     *
-     * @param s The String in format <code>0031:07:15 00:00:00</code>.
-     * @return The parsed Age object, or null if the value could not be parsed
-     */
-    @Nullable
-    public static Age fromPanasonicString(@NotNull String s)
-    {
-        if (s.length() != 19 || s.startsWith("9999:99:99"))
-            return null;
-
-        try {
-            int years = Integer.parseInt(s.substring(0, 4));
-            int months = Integer.parseInt(s.substring(5, 7));
-            int days = Integer.parseInt(s.substring(8, 10));
-            int hours = Integer.parseInt(s.substring(11, 13));
-            int minutes = Integer.parseInt(s.substring(14, 16));
-            int seconds = Integer.parseInt(s.substring(17, 19));
-
-            return new Age(years, months, days, hours, minutes, seconds);
-        }
-        catch (NumberFormatException ignored)
-        {
-            return null;
-        }
-    }
-
-    public Age(int years, int months, int days, int hours, int minutes, int seconds)
-    {
-        _years = years;
-        _months = months;
-        _days = days;
-        _hours = hours;
-        _minutes = minutes;
-        _seconds = seconds;
-    }
-
-    public int getYears()
-    {
-        return _years;
-    }
-
-    public int getMonths()
-    {
-        return _months;
-    }
-
-    public int getDays()
-    {
-        return _days;
-    }
-
-    public int getHours()
-    {
-        return _hours;
-    }
-
-    public int getMinutes()
-    {
-        return _minutes;
-    }
-
-    public int getSeconds()
-    {
-        return _seconds;
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%04d:%02d:%02d %02d:%02d:%02d", _years, _months, _days, _hours, _minutes, _seconds);
-    }
-
-    public String toFriendlyString()
-    {
-        StringBuilder result = new StringBuilder();
-        appendAgePart(result, _years, "year");
-        appendAgePart(result, _months, "month");
-        appendAgePart(result, _days, "day");
-        appendAgePart(result, _hours, "hour");
-        appendAgePart(result, _minutes, "minute");
-        appendAgePart(result, _seconds, "second");
-        return result.toString();
-    }
-
-    private static void appendAgePart(StringBuilder result, final int num, final String singularName)
-    {
-        if (num == 0)
-            return;
-        if (result.length()!=0)
-            result.append(' ');
-        result.append(num).append(' ').append(singularName);
-        if (num != 1)
-            result.append('s');
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o)
-    {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        Age age = (Age)o;
-
-        if (_days != age._days) return false;
-        if (_hours != age._hours) return false;
-        if (_minutes != age._minutes) return false;
-        if (_months != age._months) return false;
-        if (_seconds != age._seconds) return false;
-        if (_years != age._years) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        int result = _years;
-        result = 31 * result + _months;
-        result = 31 * result + _days;
-        result = 31 * result + _hours;
-        result = 31 * result + _minutes;
-        result = 31 * result + _seconds;
-        return result;
-    }
-}
Index: unk/src/com/drew/metadata/Directory.java
===================================================================
--- /trunk/src/com/drew/metadata/Directory.java	(revision 16024)
+++ 	(revision )
@@ -1,1160 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.lang.annotations.SuppressWarnings;
-
-import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Array;
-import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Abstract base class for all directory implementations, having methods for getting and setting tag values of various
- * data types.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@java.lang.SuppressWarnings("WeakerAccess")
-public abstract class Directory
-{
-    private static final String _floatFormatPattern = "0.###";
-
-    /** Map of values hashed by type identifiers. */
-    @NotNull
-    protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>();
-
-    /**
-     * A convenient list holding tag values in the order in which they were stored.
-     * This is used for creation of an iterator, and for counting the number of
-     * defined tags.
-     */
-    @NotNull
-    protected final Collection<Tag> _definedTagList = new ArrayList<Tag>();
-
-    @NotNull
-    private final Collection<String> _errorList = new ArrayList<String>(4);
-
-    /** The descriptor used to interpret tag values. */
-    protected TagDescriptor _descriptor;
-
-    @Nullable
-    private Directory _parent;
-
-// ABSTRACT METHODS
-
-    /**
-     * Provides the name of the directory, for display purposes.  E.g. <code>Exif</code>
-     *
-     * @return the name of the directory
-     */
-    @NotNull
-    public abstract String getName();
-
-    /**
-     * Provides the map of tag names, hashed by tag type identifier.
-     *
-     * @return the map of tag names
-     */
-    @NotNull
-    protected abstract HashMap<Integer, String> getTagNameMap();
-
-    protected Directory()
-    {}
-
-// VARIOUS METHODS
-
-    /**
-     * Gets a value indicating whether the directory is empty, meaning it contains no errors and no tag values.
-     */
-    public boolean isEmpty()
-    {
-        return _errorList.isEmpty() && _definedTagList.isEmpty();
-    }
-
-    /**
-     * Indicates whether the specified tag type has been set.
-     *
-     * @param tagType the tag type to check for
-     * @return true if a value exists for the specified tag type, false if not
-     */
-    @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
-    public boolean containsTag(int tagType)
-    {
-        return _tagMap.containsKey(Integer.valueOf(tagType));
-    }
-
-    /**
-     * Returns an Iterator of Tag instances that have been set in this Directory.
-     *
-     * @return an Iterator of Tag instances
-     */
-    @NotNull
-    public Collection<Tag> getTags()
-    {
-        return Collections.unmodifiableCollection(_definedTagList);
-    }
-
-    /**
-     * Returns the number of tags set in this Directory.
-     *
-     * @return the number of tags set in this Directory
-     */
-    public int getTagCount()
-    {
-        return _definedTagList.size();
-    }
-
-    /**
-     * Sets the descriptor used to interpret tag values.
-     *
-     * @param descriptor the descriptor used to interpret tag values
-     */
-    @java.lang.SuppressWarnings({ "ConstantConditions" })
-    public void setDescriptor(@NotNull TagDescriptor descriptor)
-    {
-        if (descriptor == null)
-            throw new NullPointerException("cannot set a null descriptor");
-        _descriptor = descriptor;
-    }
-
-    /**
-     * Registers an error message with this directory.
-     *
-     * @param message an error message.
-     */
-    public void addError(@NotNull String message)
-    {
-        _errorList.add(message);
-    }
-
-    /**
-     * Gets a value indicating whether this directory has any error messages.
-     *
-     * @return true if the directory contains errors, otherwise false
-     */
-    public boolean hasErrors()
-    {
-        return _errorList.size() > 0;
-    }
-
-    /**
-     * Used to iterate over any error messages contained in this directory.
-     *
-     * @return an iterable collection of error message strings.
-     */
-    @NotNull
-    public Iterable<String> getErrors()
-    {
-        return Collections.unmodifiableCollection(_errorList);
-    }
-
-    /** Returns the count of error messages in this directory. */
-    public int getErrorCount()
-    {
-        return _errorList.size();
-    }
-
-    @Nullable
-    public Directory getParent()
-    {
-        return _parent;
-    }
-
-    public void setParent(@NotNull Directory parent)
-    {
-        _parent = parent;
-    }
-
-// TAG SETTERS
-
-    /**
-     * Sets an <code>int</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as an int
-     */
-    public void setInt(int tagType, int value)
-    {
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets an <code>int[]</code> (array) for the specified tag.
-     *
-     * @param tagType the tag identifier
-     * @param ints    the int array to store
-     */
-    public void setIntArray(int tagType, @NotNull int[] ints)
-    {
-        setObjectArray(tagType, ints);
-    }
-
-    /**
-     * Sets a <code>float</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a float
-     */
-    public void setFloat(int tagType, float value)
-    {
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>float[]</code> (array) for the specified tag.
-     *
-     * @param tagType the tag identifier
-     * @param floats  the float array to store
-     */
-    public void setFloatArray(int tagType, @NotNull float[] floats)
-    {
-        setObjectArray(tagType, floats);
-    }
-
-    /**
-     * Sets a <code>double</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a double
-     */
-    public void setDouble(int tagType, double value)
-    {
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>double[]</code> (array) for the specified tag.
-     *
-     * @param tagType the tag identifier
-     * @param doubles the double array to store
-     */
-    public void setDoubleArray(int tagType, @NotNull double[] doubles)
-    {
-        setObjectArray(tagType, doubles);
-    }
-
-    /**
-     * Sets a <code>StringValue</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a StringValue
-     */
-    @java.lang.SuppressWarnings({ "ConstantConditions" })
-    public void setStringValue(int tagType, @NotNull StringValue value)
-    {
-        if (value == null)
-            throw new NullPointerException("cannot set a null StringValue");
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>String</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a String
-     */
-    @java.lang.SuppressWarnings({ "ConstantConditions" })
-    public void setString(int tagType, @NotNull String value)
-    {
-        if (value == null)
-            throw new NullPointerException("cannot set a null String");
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>String[]</code> (array) for the specified tag.
-     *
-     * @param tagType the tag identifier
-     * @param strings the String array to store
-     */
-    public void setStringArray(int tagType, @NotNull String[] strings)
-    {
-        setObjectArray(tagType, strings);
-    }
-
-    /**
-     * Sets a <code>StringValue[]</code> (array) for the specified tag.
-     *
-     * @param tagType the tag identifier
-     * @param strings the StringValue array to store
-     */
-    public void setStringValueArray(int tagType, @NotNull StringValue[] strings)
-    {
-        setObjectArray(tagType, strings);
-    }
-
-    /**
-     * Sets a <code>boolean</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a boolean
-     */
-    public void setBoolean(int tagType, boolean value)
-    {
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>long</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a long
-     */
-    public void setLong(int tagType, long value)
-    {
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>java.util.Date</code> value for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag as a java.util.Date
-     */
-    public void setDate(int tagType, @NotNull java.util.Date value)
-    {
-        setObject(tagType, value);
-    }
-
-    /**
-     * Sets a <code>Rational</code> value for the specified tag.
-     *
-     * @param tagType  the tag's value as an int
-     * @param rational rational number
-     */
-    public void setRational(int tagType, @NotNull Rational rational)
-    {
-        setObject(tagType, rational);
-    }
-
-    /**
-     * Sets a <code>Rational[]</code> (array) for the specified tag.
-     *
-     * @param tagType   the tag identifier
-     * @param rationals the Rational array to store
-     */
-    public void setRationalArray(int tagType, @NotNull Rational[] rationals)
-    {
-        setObjectArray(tagType, rationals);
-    }
-
-    /**
-     * Sets a <code>byte[]</code> (array) for the specified tag.
-     *
-     * @param tagType the tag identifier
-     * @param bytes   the byte array to store
-     */
-    public void setByteArray(int tagType, @NotNull byte[] bytes)
-    {
-        setObjectArray(tagType, bytes);
-    }
-
-    /**
-     * Sets a <code>Object</code> for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param value   the value for the specified tag
-     * @throws NullPointerException if value is <code>null</code>
-     */
-    @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" })
-    public void setObject(int tagType, @NotNull Object value)
-    {
-        if (value == null)
-            throw new NullPointerException("cannot set a null object");
-
-        if (!_tagMap.containsKey(Integer.valueOf(tagType))) {
-            _definedTagList.add(new Tag(tagType, this));
-        }
-//        else {
-//            final Object oldValue = _tagMap.get(tagType);
-//            if (!oldValue.equals(value))
-//                addError(String.format("Overwritten tag 0x%s (%s).  Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value));
-//        }
-        _tagMap.put(tagType, value);
-    }
-
-    /**
-     * Sets an array <code>Object</code> for the specified tag.
-     *
-     * @param tagType the tag's value as an int
-     * @param array   the array of values for the specified tag
-     */
-    public void setObjectArray(int tagType, @NotNull Object array)
-    {
-        // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles
-        setObject(tagType, array);
-    }
-
-// TAG GETTERS
-
-    /**
-     * Returns the specified tag's value as an int, if possible.  Every attempt to represent the tag's value as an int
-     * is taken.  Here is a list of the action taken depending upon the tag's original type:
-     * <ul>
-     * <li> int - Return unchanged.
-     * <li> Number - Return an int value (real numbers are truncated).
-     * <li> Rational - Truncate any fractional part and returns remaining int.
-     * <li> String - Attempt to parse string as an int.  If this fails, convert the char[] to an int (using shifts and OR).
-     * <li> Rational[] - Return int value of first item in array.
-     * <li> byte[] - Return int value of first item in array.
-     * <li> int[] - Return int value of first item in array.
-     * </ul>
-     *
-     * @throws MetadataException if no value exists for tagType or if it cannot be converted to an int.
-     */
-    public int getInt(int tagType) throws MetadataException
-    {
-        Integer integer = getInteger(tagType);
-        if (integer!=null)
-            return integer;
-
-        Object o = getObject(tagType);
-        if (o == null)
-            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
-        throw new MetadataException("Tag '" + tagType + "' cannot be converted to int.  It is of type '" + o.getClass() + "'.");
-    }
-
-    /**
-     * Returns the specified tag's value as an Integer, if possible.  Every attempt to represent the tag's value as an
-     * Integer is taken.  Here is a list of the action taken depending upon the tag's original type:
-     * <ul>
-     * <li> int - Return unchanged
-     * <li> Number - Return an int value (real numbers are truncated)
-     * <li> Rational - Truncate any fractional part and returns remaining int
-     * <li> String - Attempt to parse string as an int.  If this fails, convert the char[] to an int (using shifts and OR)
-     * <li> Rational[] - Return int value of first item in array if length &gt; 0
-     * <li> byte[] - Return int value of first item in array if length &gt; 0
-     * <li> int[] - Return int value of first item in array if length &gt; 0
-     * </ul>
-     *
-     * If the value is not found or cannot be converted to int, <code>null</code> is returned.
-     */
-    @Nullable
-    public Integer getInteger(int tagType)
-    {
-        Object o = getObject(tagType);
-
-        if (o == null)
-            return null;
-
-        if (o instanceof Number) {
-            return ((Number)o).intValue();
-        } else if (o instanceof String || o instanceof StringValue) {
-            try {
-                return Integer.parseInt(o.toString());
-            } catch (NumberFormatException nfe) {
-                // convert the char array to an int
-                String s = o.toString();
-                byte[] bytes = s.getBytes();
-                long val = 0;
-                for (byte aByte : bytes) {
-                    val = val << 8;
-                    val += (aByte & 0xff);
-                }
-                return (int)val;
-            }
-        } else if (o instanceof Rational[]) {
-            Rational[] rationals = (Rational[])o;
-            if (rationals.length == 1)
-                return rationals[0].intValue();
-        } else if (o instanceof byte[]) {
-            byte[] bytes = (byte[])o;
-            if (bytes.length == 1)
-                return (int)bytes[0];
-        } else if (o instanceof int[]) {
-            int[] ints = (int[])o;
-            if (ints.length == 1)
-                return ints[0];
-        } else if (o instanceof short[]) {
-            short[] shorts = (short[])o;
-            if (shorts.length == 1)
-                return (int)shorts[0];
-        }
-        return null;
-    }
-
-    /**
-     * Gets the specified tag's value as a String array, if possible.  Only supported
-     * where the tag is set as StringValue[], String[], StringValue, String, int[], byte[] or Rational[].
-     *
-     * @param tagType the tag identifier
-     * @return the tag's value as an array of Strings. If the value is unset or cannot be converted, <code>null</code> is returned.
-     */
-    @Nullable
-    public String[] getStringArray(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof String[])
-            return (String[])o;
-        if (o instanceof String)
-            return new String[] { (String)o };
-        if (o instanceof StringValue)
-            return new String[] { o.toString() };
-        if (o instanceof StringValue[]) {
-            StringValue[] stringValues = (StringValue[])o;
-            String[] strings = new String[stringValues.length];
-            for (int i = 0; i < strings.length; i++)
-                strings[i] = stringValues[i].toString();
-            return strings;
-        }
-        if (o instanceof int[]) {
-            int[] ints = (int[])o;
-            String[] strings = new String[ints.length];
-            for (int i = 0; i < strings.length; i++)
-                strings[i] = Integer.toString(ints[i]);
-            return strings;
-        }
-        if (o instanceof byte[]) {
-            byte[] bytes = (byte[])o;
-            String[] strings = new String[bytes.length];
-            for (int i = 0; i < strings.length; i++)
-                strings[i] = Byte.toString(bytes[i]);
-            return strings;
-        }
-        if (o instanceof Rational[]) {
-            Rational[] rationals = (Rational[])o;
-            String[] strings = new String[rationals.length];
-            for (int i = 0; i < strings.length; i++)
-                strings[i] = rationals[i].toSimpleString(false);
-            return strings;
-        }
-        return null;
-    }
-
-    /**
-     * Gets the specified tag's value as a StringValue array, if possible.
-     * Only succeeds if the tag is set as StringValue[], or StringValue.
-     *
-     * @param tagType the tag identifier
-     * @return the tag's value as an array of StringValues. If the value is unset or cannot be converted, <code>null</code> is returned.
-     */
-    @Nullable
-    public StringValue[] getStringValueArray(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof StringValue[])
-            return (StringValue[])o;
-        if (o instanceof StringValue)
-            return new StringValue[] {(StringValue) o};
-        return null;
-    }
-
-    /**
-     * Gets the specified tag's value as an int array, if possible.  Only supported
-     * where the tag is set as String, Integer, int[], byte[] or Rational[].
-     *
-     * @param tagType the tag identifier
-     * @return the tag's value as an int array
-     */
-    @Nullable
-    public int[] getIntArray(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof int[])
-            return (int[])o;
-        if (o instanceof Rational[]) {
-            Rational[] rationals = (Rational[])o;
-            int[] ints = new int[rationals.length];
-            for (int i = 0; i < ints.length; i++) {
-                ints[i] = rationals[i].intValue();
-            }
-            return ints;
-        }
-        if (o instanceof short[]) {
-            short[] shorts = (short[])o;
-            int[] ints = new int[shorts.length];
-            for (int i = 0; i < shorts.length; i++) {
-                ints[i] = shorts[i];
-            }
-            return ints;
-        }
-        if (o instanceof byte[]) {
-            byte[] bytes = (byte[])o;
-            int[] ints = new int[bytes.length];
-            for (int i = 0; i < bytes.length; i++) {
-                ints[i] = bytes[i];
-            }
-            return ints;
-        }
-        if (o instanceof CharSequence) {
-            CharSequence str = (CharSequence)o;
-            int[] ints = new int[str.length()];
-            for (int i = 0; i < str.length(); i++) {
-                ints[i] = str.charAt(i);
-            }
-            return ints;
-        }
-        if (o instanceof Integer)
-            return new int[] { (Integer)o };
-
-        return null;
-    }
-
-    /**
-     * Gets the specified tag's value as an byte array, if possible.  Only supported
-     * where the tag is set as String, Integer, int[], byte[] or Rational[].
-     *
-     * @param tagType the tag identifier
-     * @return the tag's value as a byte array
-     */
-    @Nullable
-    public byte[] getByteArray(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null) {
-            return null;
-        } else if (o instanceof StringValue) {
-            return ((StringValue)o).getBytes();
-        } else if (o instanceof Rational[]) {
-            Rational[] rationals = (Rational[])o;
-            byte[] bytes = new byte[rationals.length];
-            for (int i = 0; i < bytes.length; i++) {
-                bytes[i] = rationals[i].byteValue();
-            }
-            return bytes;
-        } else if (o instanceof byte[]) {
-            return (byte[])o;
-        } else if (o instanceof int[]) {
-            int[] ints = (int[])o;
-            byte[] bytes = new byte[ints.length];
-            for (int i = 0; i < ints.length; i++) {
-                bytes[i] = (byte)ints[i];
-            }
-            return bytes;
-        } else if (o instanceof short[]) {
-            short[] shorts = (short[])o;
-            byte[] bytes = new byte[shorts.length];
-            for (int i = 0; i < shorts.length; i++) {
-                bytes[i] = (byte)shorts[i];
-            }
-            return bytes;
-        } else if (o instanceof CharSequence) {
-            CharSequence str = (CharSequence)o;
-            byte[] bytes = new byte[str.length()];
-            for (int i = 0; i < str.length(); i++) {
-                bytes[i] = (byte)str.charAt(i);
-            }
-            return bytes;
-        }
-        if (o instanceof Integer)
-            return new byte[] { ((Integer)o).byteValue() };
-
-        return null;
-    }
-
-    /** Returns the specified tag's value as a double, if possible. */
-    public double getDouble(int tagType) throws MetadataException
-    {
-        Double value = getDoubleObject(tagType);
-        if (value!=null)
-            return value;
-        Object o = getObject(tagType);
-        if (o == null)
-            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
-        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double.  It is of type '" + o.getClass() + "'.");
-    }
-    /** Returns the specified tag's value as a Double.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
-    @Nullable
-    public Double getDoubleObject(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof String || o instanceof StringValue) {
-            try {
-                return Double.parseDouble(o.toString());
-            } catch (NumberFormatException nfe) {
-                return null;
-            }
-        }
-        if (o instanceof Number)
-            return ((Number)o).doubleValue();
-
-        return null;
-    }
-
-    /** Returns the specified tag's value as a float, if possible. */
-    public float getFloat(int tagType) throws MetadataException
-    {
-        Float value = getFloatObject(tagType);
-        if (value!=null)
-            return value;
-        Object o = getObject(tagType);
-        if (o == null)
-            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
-        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float.  It is of type '" + o.getClass() + "'.");
-    }
-
-    /** Returns the specified tag's value as a float.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
-    @Nullable
-    public Float getFloatObject(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof String || o instanceof StringValue) {
-            try {
-                return Float.parseFloat(o.toString());
-            } catch (NumberFormatException nfe) {
-                return null;
-            }
-        }
-        if (o instanceof Number)
-            return ((Number)o).floatValue();
-        return null;
-    }
-
-    /** Returns the specified tag's value as a long, if possible. */
-    public long getLong(int tagType) throws MetadataException
-    {
-        Long value = getLongObject(tagType);
-        if (value != null)
-            return value;
-        Object o = getObject(tagType);
-        if (o == null)
-            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
-        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long.  It is of type '" + o.getClass() + "'.");
-    }
-
-    /** Returns the specified tag's value as a long.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
-    @Nullable
-    public Long getLongObject(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof Number)
-            return ((Number)o).longValue();
-        if (o instanceof String || o instanceof StringValue) {
-            try {
-                return Long.parseLong(o.toString());
-            } catch (NumberFormatException nfe) {
-                return null;
-            }
-        } else if (o instanceof Rational[]) {
-            Rational[] rationals = (Rational[])o;
-            if (rationals.length == 1)
-                return rationals[0].longValue();
-        } else if (o instanceof byte[]) {
-            byte[] bytes = (byte[])o;
-            if (bytes.length == 1)
-                return (long)bytes[0];
-        } else if (o instanceof int[]) {
-            int[] ints = (int[])o;
-            if (ints.length == 1)
-                return (long)ints[0];
-        } else if (o instanceof short[]) {
-            short[] shorts = (short[])o;
-            if (shorts.length == 1)
-                return (long)shorts[0];
-        }
-        return null;
-    }
-
-    /** Returns the specified tag's value as a boolean, if possible. */
-    public boolean getBoolean(int tagType) throws MetadataException
-    {
-        Boolean value = getBooleanObject(tagType);
-        if (value != null)
-            return value;
-        Object o = getObject(tagType);
-        if (o == null)
-            throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first");
-        throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean.  It is of type '" + o.getClass() + "'.");
-    }
-
-    /** Returns the specified tag's value as a boolean.  If the tag is not set or cannot be converted, <code>null</code> is returned. */
-    @Nullable
-    @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent")
-    public Boolean getBooleanObject(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-        if (o instanceof Boolean)
-            return (Boolean)o;
-        if (o instanceof String || o instanceof StringValue) {
-            try {
-                return Boolean.getBoolean(o.toString());
-            } catch (NumberFormatException nfe) {
-                return null;
-            }
-        }
-        if (o instanceof Number)
-            return (((Number)o).doubleValue() != 0);
-        return null;
-    }
-
-    /**
-     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
-     * <p>
-     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
-     * the GMT {@link TimeZone}.  If the {@link TimeZone} is known, call the overload that accepts one as an argument.
-     */
-    @Nullable
-    public java.util.Date getDate(int tagType)
-    {
-        return getDate(tagType, null, null);
-    }
-
-    /**
-     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
-     * <p>
-     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
-     * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).  Note that this parameter
-     * is only considered if the underlying value is a string and it has no time zone information, otherwise it has no effect.
-     */
-    @Nullable
-    public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone)
-    {
-        return getDate(tagType, null, timeZone);
-    }
-
-    /**
-     * Returns the specified tag's value as a java.util.Date.  If the value is unset or cannot be converted, <code>null</code> is returned.
-     * <p>
-     * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in
-     * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).  Note that this parameter
-     * is only considered if the underlying value is a string and it has no time zone information, otherwise it has no effect.
-     * In addition, the {@code subsecond} parameter, which specifies the number of digits after the decimal point in the seconds,
-     * is set to the returned Date. This parameter is only considered if the underlying value is a string and is has
-     * no subsecond information, otherwise it has no effect.
-     *
-     * @param tagType the tag identifier
-     * @param subsecond the subsecond value for the Date
-     * @param timeZone the time zone to use
-     * @return a Date representing the time value
-     */
-    @Nullable
-    public java.util.Date getDate(int tagType, @Nullable String subsecond, @Nullable TimeZone timeZone)
-    {
-        Object o = getObject(tagType);
-
-        if (o instanceof java.util.Date)
-            return (java.util.Date)o;
-
-        java.util.Date date = null;
-
-        if ((o instanceof String) || (o instanceof StringValue)) {
-            // This seems to cover all known Exif and Xmp date strings
-            // Note that "    :  :     :  :  " is a valid date string according to the Exif spec (which means 'unknown date'): http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html
-            String datePatterns[] = {
-                    "yyyy:MM:dd HH:mm:ss",
-                    "yyyy:MM:dd HH:mm",
-                    "yyyy-MM-dd HH:mm:ss",
-                    "yyyy-MM-dd HH:mm",
-                    "yyyy.MM.dd HH:mm:ss",
-                    "yyyy.MM.dd HH:mm",
-                    "yyyy-MM-dd'T'HH:mm:ss",
-                    "yyyy-MM-dd'T'HH:mm",
-                    "yyyy-MM-dd",
-                    "yyyy-MM",
-                    "yyyyMMdd", // as used in IPTC data
-                    "yyyy" };
-
-            String dateString = o.toString();
-
-            // if the date string has subsecond information, it supersedes the subsecond parameter
-            Pattern subsecondPattern = Pattern.compile("(\\d\\d:\\d\\d:\\d\\d)(\\.\\d+)");
-            Matcher subsecondMatcher = subsecondPattern.matcher(dateString);
-            if (subsecondMatcher.find()) {
-                subsecond = subsecondMatcher.group(2).substring(1);
-                dateString = subsecondMatcher.replaceAll("$1");
-            }
-
-            // if the date string has time zone information, it supersedes the timeZone parameter
-            Pattern timeZonePattern = Pattern.compile("(Z|[+-]\\d\\d:\\d\\d)$");
-            Matcher timeZoneMatcher = timeZonePattern.matcher(dateString);
-            if (timeZoneMatcher.find()) {
-                timeZone = TimeZone.getTimeZone("GMT" + timeZoneMatcher.group().replaceAll("Z", ""));
-                dateString = timeZoneMatcher.replaceAll("");
-            }
-
-            for (String datePattern : datePatterns) {
-                try {
-                    DateFormat parser = new SimpleDateFormat(datePattern);
-                    if (timeZone != null)
-                        parser.setTimeZone(timeZone);
-                    else
-                        parser.setTimeZone(TimeZone.getTimeZone("GMT")); // don't interpret zone time
-
-                    date = parser.parse(dateString);
-                    break;
-                } catch (ParseException ex) {
-                    // simply try the next pattern
-                }
-            }
-        }
-
-        if (date == null)
-            return null;
-
-        if (subsecond == null)
-            return date;
-
-        try {
-            int millisecond = (int) (Double.parseDouble("." + subsecond) * 1000);
-            if (millisecond >= 0 && millisecond < 1000) {
-                Calendar calendar = Calendar.getInstance();
-                calendar.setTime(date);
-                calendar.set(Calendar.MILLISECOND, millisecond);
-                return calendar.getTime();
-            }
-            return date;
-        } catch (NumberFormatException e) {
-            return date;
-        }
-    }
-
-    /** Returns the specified tag's value as a Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
-    @Nullable
-    public Rational getRational(int tagType)
-    {
-        Object o = getObject(tagType);
-
-        if (o == null)
-            return null;
-
-        if (o instanceof Rational)
-            return (Rational)o;
-        if (o instanceof Integer)
-            return new Rational((Integer)o, 1);
-        if (o instanceof Long)
-            return new Rational((Long)o, 1);
-
-        // NOTE not doing conversions for real number types
-
-        return null;
-    }
-
-    /** Returns the specified tag's value as an array of Rational.  If the value is unset or cannot be converted, <code>null</code> is returned. */
-    @Nullable
-    public Rational[] getRationalArray(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-
-        if (o instanceof Rational[])
-            return (Rational[])o;
-
-        return null;
-    }
-
-    /**
-     * Returns the specified tag's value as a String.  This value is the 'raw' value.  A more presentable decoding
-     * of this value may be obtained from the corresponding Descriptor.
-     *
-     * @return the String representation of the tag's value, or
-     *         <code>null</code> if the tag hasn't been defined.
-     */
-    @Nullable
-    public String getString(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o == null)
-            return null;
-
-        if (o instanceof Rational)
-            return ((Rational)o).toSimpleString(true);
-
-        if (o.getClass().isArray()) {
-            // handle arrays of objects and primitives
-            int arrayLength = Array.getLength(o);
-            final Class<?> componentType = o.getClass().getComponentType();
-
-            StringBuilder string = new StringBuilder();
-
-            if (Object.class.isAssignableFrom(componentType)) {
-                // object array
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    string.append(Array.get(o, i).toString());
-                }
-            } else if (componentType.getName().equals("int")) {
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    string.append(Array.getInt(o, i));
-                }
-            } else if (componentType.getName().equals("short")) {
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    string.append(Array.getShort(o, i));
-                }
-            } else if (componentType.getName().equals("long")) {
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    string.append(Array.getLong(o, i));
-                }
-            } else if (componentType.getName().equals("float")) {
-                DecimalFormat format = new DecimalFormat(_floatFormatPattern);
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    String s = format.format(Array.getFloat(o, i));
-                    string.append(s.equals("-0") ? "0" : s);
-                }
-            } else if (componentType.getName().equals("double")) {
-                DecimalFormat format = new DecimalFormat(_floatFormatPattern);
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    String s = format.format(Array.getDouble(o, i));
-                    string.append(s.equals("-0") ? "0" : s);
-                }
-            } else if (componentType.getName().equals("byte")) {
-                for (int i = 0; i < arrayLength; i++) {
-                    if (i != 0)
-                        string.append(' ');
-                    string.append(Array.getByte(o, i) & 0xff);
-                }
-            } else {
-                addError("Unexpected array component type: " + componentType.getName());
-            }
-
-            return string.toString();
-        }
-
-        if (o instanceof Double)
-            return new DecimalFormat(_floatFormatPattern).format(((Double)o).doubleValue());
-
-        if (o instanceof Float)
-            return new DecimalFormat(_floatFormatPattern).format(((Float)o).floatValue());
-
-        // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show
-        // the actual data within the file.  It is not inconceivable that whitespace may be significant here, so we
-        // do not trim.  Also, if support is added for writing data back to files, this may cause issues.
-        // We leave trimming to the presentation layer.
-        return o.toString();
-    }
-
-    @Nullable
-    public String getString(int tagType, String charset)
-    {
-        byte[] bytes = getByteArray(tagType);
-        if (bytes==null)
-            return null;
-        try {
-            return new String(bytes, charset);
-        } catch (UnsupportedEncodingException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public StringValue getStringValue(int tagType)
-    {
-        Object o = getObject(tagType);
-        if (o instanceof StringValue)
-            return (StringValue)o;
-        return null;
-    }
-
-    /**
-     * Returns the object hashed for the particular tag type specified, if available.
-     *
-     * @param tagType the tag type identifier
-     * @return the tag's value as an Object if available, else <code>null</code>
-     */
-    @java.lang.SuppressWarnings({ "UnnecessaryBoxing" })
-    @Nullable
-    public Object getObject(int tagType)
-    {
-        return _tagMap.get(Integer.valueOf(tagType));
-    }
-
-// OTHER METHODS
-
-    /**
-     * Returns the name of a specified tag as a String.
-     *
-     * @param tagType the tag type identifier
-     * @return the tag's name as a String
-     */
-    @NotNull
-    public String getTagName(int tagType)
-    {
-        HashMap<Integer, String> nameMap = getTagNameMap();
-        if (!nameMap.containsKey(tagType)) {
-            String hex = Integer.toHexString(tagType);
-            while (hex.length() < 4) {
-                hex = "0" + hex;
-            }
-            return "Unknown tag (0x" + hex + ")";
-        }
-        return nameMap.get(tagType);
-    }
-
-    /**
-     * Gets whether the specified tag is known by the directory and has a name.
-     *
-     * @param tagType the tag type identifier
-     * @return whether this directory has a name for the specified tag
-     */
-    public boolean hasTagName(int tagType)
-    {
-        return getTagNameMap().containsKey(tagType);
-    }
-
-    /**
-     * Provides a description of a tag's value using the descriptor set by
-     * <code>setDescriptor(Descriptor)</code>.
-     *
-     * @param tagType the tag type identifier
-     * @return the tag value's description as a String
-     */
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        assert(_descriptor != null);
-        return _descriptor.getDescription(tagType);
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("%s Directory (%d %s)",
-            getName(),
-            _tagMap.size(),
-            _tagMap.size() == 1
-                ? "tag"
-                : "tags");
-    }
-}
Index: unk/src/com/drew/metadata/ErrorDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/ErrorDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,75 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.annotations.NotNull;
-import java.util.*;
-
-/**
- * A directory to use for the reporting of errors. No values may be added to this directory, only warnings and errors.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-
-public final class ErrorDirectory extends Directory
-{
-
-    public ErrorDirectory()
-    {}
-
-    public ErrorDirectory(String error)
-    {
-        super.addError(error);
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Error";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return new HashMap<Integer, String>();
-    }
-
-    @Override
-    @NotNull
-    public String getTagName(int tagType)
-    {
-        return "";
-    }
-
-    @Override
-    public boolean hasTagName(int tagType)
-    {
-        return false;
-    }
-
-    @Override
-    public void setObject(int tagType, @NotNull Object value)
-    {
-        throw new UnsupportedOperationException(String.format("Cannot add value to %s.", ErrorDirectory.class.getName()));
-    }
-}
Index: unk/src/com/drew/metadata/Face.java
===================================================================
--- /trunk/src/com/drew/metadata/Face.java	(revision 16024)
+++ 	(revision )
@@ -1,133 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * Class to hold information about a detected or recognized face in a photo.
- * <p>
- * When a face is <em>detected</em>, the camera believes that a face is present at a given location in
- * the image, but is not sure whose face it is.  When a face is <em>recognised</em>, then the face is
- * both detected and identified as belonging to a known person.
- *
- * @author Philipp Sandhaus, Drew Noakes
- */
-public class Face
-{
-    private final int _x;
-    private final int _y;
-    private final int _width;
-    private final int _height;
-    @Nullable
-    private final String _name;
-    @Nullable
-    private final Age _age;
-
-    public Face(int x, int y, int width, int height, @Nullable String name, @Nullable Age age)
-    {
-        _x = x;
-        _y = y;
-        _width = width;
-        _height = height;
-        _name = name;
-        _age = age;
-    }
-
-    public int getX()
-    {
-        return _x;
-    }
-
-    public int getY()
-    {
-        return _y;
-    }
-
-    public int getWidth()
-    {
-        return _width;
-    }
-
-    public int getHeight()
-    {
-        return _height;
-    }
-
-    @Nullable
-    public String getName()
-    {
-        return _name;
-    }
-
-    @Nullable
-    public Age getAge()
-    {
-        return _age;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o)
-    {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        Face face = (Face)o;
-
-        if (_height != face._height) return false;
-        if (_width != face._width) return false;
-        if (_x != face._x) return false;
-        if (_y != face._y) return false;
-        if (_age != null ? !_age.equals(face._age) : face._age != null) return false;
-        if (_name != null ? !_name.equals(face._name) : face._name != null) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode()
-    {
-        int result = _x;
-        result = 31 * result + _y;
-        result = 31 * result + _width;
-        result = 31 * result + _height;
-        result = 31 * result + (_name != null ? _name.hashCode() : 0);
-        result = 31 * result + (_age != null ? _age.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    @NotNull
-    public String toString()
-    {
-        StringBuilder result = new StringBuilder();
-        result.append("x: ").append(_x);
-        result.append(" y: ").append(_y);
-        result.append(" width: ").append(_width);
-        result.append(" height: ").append(_height);
-        if (_name != null)
-            result.append(" name: ").append(_name);
-        if (_age != null)
-            result.append(" age: ").append(_age.toFriendlyString());
-        return result.toString();
-    }
-}
Index: unk/src/com/drew/metadata/Metadata.java
===================================================================
--- /trunk/src/com/drew/metadata/Metadata.java	(revision 16024)
+++ 	(revision )
@@ -1,147 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.util.*;
-
-/**
- * A top-level object that holds the metadata values extracted from an image.
- * <p>
- * Metadata objects may contain zero or more {@link Directory} objects.  Each directory may contain zero or more tags
- * with corresponding values.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public final class Metadata
-{
-    /**
-     * The list of {@link Directory} instances in this container, in the order they were added.
-     */
-    @NotNull
-    private final List<Directory> _directories = new ArrayList<>();
-
-    /**
-     * Returns an iterable set of the {@link Directory} instances contained in this metadata collection.
-     *
-     * @return an iterable set of directories
-     */
-    @NotNull
-    public Iterable<Directory> getDirectories()
-    {
-        return _directories;
-    }
-
-    @NotNull
-    @SuppressWarnings("unchecked")
-    public <T extends Directory> Collection<T> getDirectoriesOfType(Class<T> type)
-    {
-        List<T> directories = new ArrayList<>();
-        for (Directory dir : _directories) {
-            if (type.isAssignableFrom(dir.getClass())) {
-                directories.add((T)dir);
-            }
-        }
-        return directories;
-    }
-
-    /**
-     * Returns the count of directories in this metadata collection.
-     *
-     * @return the number of unique directory types set for this metadata collection
-     */
-    public int getDirectoryCount()
-    {
-        return _directories.size();
-    }
-
-    /**
-     * Adds a directory to this metadata collection.
-     *
-     * @param directory the {@link Directory} to add into this metadata collection.
-     */
-    public <T extends Directory> void addDirectory(@NotNull T directory)
-    {
-        _directories.add(directory);
-    }
-
-    /**
-     * Gets the first {@link Directory} of the specified type contained within this metadata collection.
-     * If no instances of this type are present, <code>null</code> is returned.
-     *
-     * @param type the Directory type
-     * @param <T> the Directory type
-     * @return the first Directory of type T in this metadata collection, or <code>null</code> if none exist
-     */
-    @Nullable
-    @SuppressWarnings("unchecked")
-    public <T extends Directory> T getFirstDirectoryOfType(@NotNull Class<T> type)
-    {
-        for (Directory dir : _directories) {
-            if (type.isAssignableFrom(dir.getClass()))
-                return (T)dir;
-        }
-        return null;
-    }
-
-    /**
-     * Indicates whether an instance of the given directory type exists in this Metadata instance.
-     *
-     * @param type the {@link Directory} type
-     * @return <code>true</code> if a {@link Directory} of the specified type exists, otherwise <code>false</code>
-     */
-    public boolean containsDirectoryOfType(Class<? extends Directory> type)
-    {
-        for (Directory dir : _directories) {
-            if (type.isAssignableFrom(dir.getClass()))
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Indicates whether any errors were reported during the reading of metadata values.
-     * This value will be true if Directory.hasErrors() is true for one of the contained {@link Directory} objects.
-     *
-     * @return whether one of the contained directories has an error
-     */
-    public boolean hasErrors()
-    {
-        for (Directory directory : getDirectories()) {
-            if (directory.hasErrors())
-                return true;
-        }
-        return false;
-    }
-
-    @Override
-    public String toString()
-    {
-        int count = getDirectoryCount();
-        return String.format("Metadata (%d %s)",
-            count,
-            count == 1
-                ? "directory"
-                : "directories");
-    }
-}
Index: unk/src/com/drew/metadata/MetadataException.java
===================================================================
--- /trunk/src/com/drew/metadata/MetadataException.java	(revision 16024)
+++ 	(revision )
@@ -1,49 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.CompoundException;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * Base class for all metadata specific exceptions.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class MetadataException extends CompoundException
-{
-    private static final long serialVersionUID = 8612756143363919682L;
-
-    public MetadataException(@Nullable String msg)
-    {
-        super(msg);
-    }
-
-    public MetadataException(@Nullable Throwable exception)
-    {
-        super(exception);
-    }
-
-    public MetadataException(@Nullable String msg, @Nullable Throwable innerException)
-    {
-        super(msg, innerException);
-    }
-}
Index: unk/src/com/drew/metadata/MetadataReader.java
===================================================================
--- /trunk/src/com/drew/metadata/MetadataReader.java	(revision 16024)
+++ 	(revision )
@@ -1,42 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-
-/**
- * Defines an object capable of processing a particular type of metadata from a {@link RandomAccessReader}.
- * <p>
- * Instances of this interface must be thread-safe and reusable.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public interface MetadataReader
-{
-    /**
-     * Extracts metadata from <code>reader</code> and merges it into the specified {@link Metadata} object.
-     *
-     * @param reader   The {@link RandomAccessReader} from which the metadata should be extracted.
-     * @param metadata The {@link Metadata} object into which extracted values should be merged.
-     */
-    void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata);
-}
Index: unk/src/com/drew/metadata/StringValue.java
===================================================================
--- /trunk/src/com/drew/metadata/StringValue.java	(revision 16024)
+++ 	(revision )
@@ -1,76 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-public final class StringValue
-{
-    @NotNull
-    private final byte[] _bytes;
-
-    @Nullable
-    private final Charset _charset;
-
-    public StringValue(@NotNull byte[] bytes, @Nullable Charset charset)
-    {
-        _bytes = bytes;
-        _charset = charset;
-    }
-
-    @NotNull
-    public byte[] getBytes()
-    {
-        return _bytes;
-    }
-
-    @Nullable
-    public Charset getCharset()
-    {
-        return _charset;
-    }
-
-    @Override
-    public String toString()
-    {
-        return toString(_charset);
-    }
-
-    public String toString(@Nullable Charset charset)
-    {
-        if (charset != null) {
-            try {
-                return new String(_bytes, charset.name());
-            } catch (UnsupportedEncodingException ex) {
-                // fall through
-            }
-        }
-
-        return new String(_bytes);
-    }
-}
Index: unk/src/com/drew/metadata/Tag.java
===================================================================
--- /trunk/src/com/drew/metadata/Tag.java	(revision 16024)
+++ 	(revision )
@@ -1,130 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-/**
- * Models a particular tag within a {@link com.drew.metadata.Directory} and provides methods for obtaining its value.
- * Immutable.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("unused")
-public class Tag
-{
-    private final int _tagType;
-    @NotNull
-    private final Directory _directory;
-
-    public Tag(int tagType, @NotNull Directory directory)
-    {
-        _tagType = tagType;
-        _directory = directory;
-    }
-
-    /**
-     * Gets the tag type as an int
-     *
-     * @return the tag type as an int
-     */
-    public int getTagType()
-    {
-        return _tagType;
-    }
-
-    /**
-     * Gets the tag type in hex notation as a String with padded leading
-     * zeroes if necessary (i.e. <code>0x100e</code>).
-     *
-     * @return the tag type as a string in hexadecimal notation
-     */
-    @NotNull
-    public String getTagTypeHex()
-    {
-        return String.format("0x%04x", _tagType);
-    }
-
-    /**
-     * Get a description of the tag's value, considering enumerated values
-     * and units.
-     *
-     * @return a description of the tag's value
-     */
-    @Nullable
-    public String getDescription()
-    {
-        return _directory.getDescription(_tagType);
-    }
-
-    /**
-     * Get whether this tag has a name.
-     *
-     * If <code>true</code>, it may be accessed via {@link #getTagName}.
-     * If <code>false</code>, {@link #getTagName} will return a string resembling <code>"Unknown tag (0x1234)"</code>.
-     *
-     * @return whether this tag has a name
-     */
-    public boolean hasTagName()
-    {
-        return _directory.hasTagName(_tagType);
-    }
-
-    /**
-     * Get the name of the tag, such as <code>Aperture</code>, or
-     * <code>InteropVersion</code>.
-     *
-     * @return the tag's name
-     */
-    @NotNull
-    public String getTagName()
-    {
-        return _directory.getTagName(_tagType);
-    }
-
-    /**
-     * Get the name of the {@link com.drew.metadata.Directory} in which the tag exists, such as
-     * <code>Exif</code>, <code>GPS</code> or <code>Interoperability</code>.
-     *
-     * @return name of the {@link com.drew.metadata.Directory} in which this tag exists
-     */
-    @NotNull
-    public String getDirectoryName()
-    {
-        return _directory.getName();
-    }
-
-    /**
-     * A basic representation of the tag's type and value.  EG: <code>[Exif IFD0] FNumber - f/2.8</code>.
-     *
-     * @return the tag's type and value
-     */
-    @Override
-    @NotNull
-    public String toString()
-    {
-        String description = getDescription();
-        if (description == null)
-            description = _directory.getString(getTagType()) + " (unable to formulate description)";
-        return "[" + _directory.getName() + "] " + getTagName() + " - " + description;
-    }
-}
Index: unk/src/com/drew/metadata/TagDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/TagDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,506 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata;
-
-import com.drew.lang.Rational;
-import com.drew.lang.StringUtil;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Array;
-import java.math.RoundingMode;
-import java.nio.charset.Charset;
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Base class for all tag descriptor classes.  Implementations are responsible for
- * providing the human-readable string representation of tag values stored in a directory.
- * The directory is provided to the tag descriptor via its constructor.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class TagDescriptor<T extends Directory>
-{
-    @NotNull
-    protected final T _directory;
-
-    public TagDescriptor(@NotNull T directory)
-    {
-        _directory = directory;
-    }
-
-    /**
-     * Returns a descriptive value of the specified tag for this image.
-     * Where possible, known values will be substituted here in place of the raw
-     * tokens actually kept in the metadata segment.  If no substitution is
-     * available, the value provided by <code>getString(tagType)</code> will be returned.
-     *
-     * @param tagType the tag to find a description for
-     * @return a description of the image's value for the specified tag, or
-     *         <code>null</code> if the tag hasn't been defined.
-     */
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        Object object = _directory.getObject(tagType);
-
-        if (object == null)
-            return null;
-
-        // special presentation for long arrays
-        if (object.getClass().isArray()) {
-            final int length = Array.getLength(object);
-            if (length > 16) {
-                return String.format("[%d values]", length);
-            }
-        }
-
-        if (object instanceof Date) {
-            // Produce a date string having a format that includes the offset in form "+00:00"
-            return new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy")
-                .format((Date) object)
-                .replaceAll("([0-9]{2} [^ ]+)$", ":$1");
-        }
-
-        // no special handling required, so use default conversion to a string
-        return _directory.getString(tagType);
-    }
-
-    /**
-     * Takes a series of 4 bytes from the specified offset, and converts these to a
-     * well-known version number, where possible.
-     * <p>
-     * Two different formats are processed:
-     * <ul>
-     * <li>[30 32 31 30] -&gt; 2.10</li>
-     * <li>[0 1 0 0] -&gt; 1.00</li>
-     * </ul>
-     *
-     * @param components  the four version values
-     * @param majorDigits the number of components to be
-     * @return the version as a string of form "2.10" or null if the argument cannot be converted
-     */
-    @Nullable
-    public static String convertBytesToVersionString(@Nullable int[] components, final int majorDigits)
-    {
-        if (components == null)
-            return null;
-        StringBuilder version = new StringBuilder();
-        for (int i = 0; i < 4 && i < components.length; i++) {
-            if (i == majorDigits)
-                version.append('.');
-            char c = (char)components[i];
-            if (c < '0')
-                c += '0';
-            if (i == 0 && c == '0')
-                continue;
-            version.append(c);
-        }
-        return version.toString();
-    }
-
-    @Nullable
-    protected String getVersionBytesDescription(final int tagType, int majorDigits)
-    {
-        int[] values = _directory.getIntArray(tagType);
-        return values == null ? null : convertBytesToVersionString(values, majorDigits);
-    }
-
-    @Nullable
-    protected String getIndexedDescription(final int tagType, @NotNull String... descriptions)
-    {
-        return getIndexedDescription(tagType, 0, descriptions);
-    }
-
-    @Nullable
-    protected String getIndexedDescription(final int tagType, final int baseIndex, @NotNull String... descriptions)
-    {
-        final Long index = _directory.getLongObject(tagType);
-        if (index == null)
-            return null;
-        final long arrayIndex = index - baseIndex;
-        if (arrayIndex >= 0 && arrayIndex < descriptions.length) {
-            String description = descriptions[(int)arrayIndex];
-            if (description != null)
-                return description;
-        }
-        return "Unknown (" + index + ")";
-    }
-
-    @Nullable
-    protected String getByteLengthDescription(final int tagType)
-    {
-        byte[] bytes = _directory.getByteArray(tagType);
-        if (bytes == null)
-            return null;
-        return String.format("(%d byte%s)", bytes.length, bytes.length == 1 ? "" : "s");
-    }
-
-    @Nullable
-    protected String getSimpleRational(final int tagType)
-    {
-        Rational value = _directory.getRational(tagType);
-        if (value == null)
-            return null;
-        return value.toSimpleString(true);
-    }
-
-    @Nullable
-    protected String getDecimalRational(final int tagType, final int decimalPlaces)
-    {
-        Rational value = _directory.getRational(tagType);
-        if (value == null)
-            return null;
-        return String.format("%." + decimalPlaces + "f", value.doubleValue());
-    }
-
-    @Nullable
-    protected String getFormattedInt(final int tagType, @NotNull final String format)
-    {
-        Integer value = _directory.getInteger(tagType);
-        if (value == null)
-            return null;
-        return String.format(format, value);
-    }
-
-    @Nullable
-    protected String getFormattedFloat(final int tagType, @NotNull final String format)
-    {
-        Float value = _directory.getFloatObject(tagType);
-        if (value == null)
-            return null;
-        return String.format(format, value);
-    }
-
-    @Nullable
-    protected String getFormattedString(final int tagType, @NotNull final String format)
-    {
-        String value = _directory.getString(tagType);
-        if (value == null)
-            return null;
-        return String.format(format, value);
-    }
-
-    @Nullable
-    protected String getEpochTimeDescription(final int tagType)
-    {
-        // TODO have observed a byte[8] here which is likely some kind of date (ticks as long?)
-        Long value = _directory.getLongObject(tagType);
-        if (value == null)
-            return null;
-        return new Date(value).toString();
-    }
-
-    /**
-     * LSB first. Labels may be null, a String, or a String[2] with (low label,high label) values.
-     */
-    @Nullable
-    protected String getBitFlagDescription(final int tagType, @NotNull final Object... labels)
-    {
-        Integer value = _directory.getInteger(tagType);
-
-        if (value == null)
-            return null;
-
-        List<String> parts = new ArrayList<>();
-
-        int bitIndex = 0;
-        while (labels.length > bitIndex) {
-            Object labelObj = labels[bitIndex];
-            if (labelObj != null) {
-                boolean isBitSet = (value & 1) == 1;
-                if (labelObj instanceof String[]) {
-                    String[] labelPair = (String[])labelObj;
-                    assert(labelPair.length == 2);
-                    parts.add(labelPair[isBitSet ? 1 : 0]);
-                } else if (isBitSet && labelObj instanceof String) {
-                    parts.add((String)labelObj);
-                }
-            }
-            value >>= 1;
-            bitIndex++;
-        }
-
-        return StringUtil.join(parts, ", ");
-    }
-
-    @Nullable
-    protected String get7BitStringFromBytes(final int tagType)
-    {
-        final byte[] bytes = _directory.getByteArray(tagType);
-
-        if (bytes == null)
-            return null;
-
-        int length = bytes.length;
-        for (int index = 0; index < bytes.length; index++) {
-            int i = bytes[index] & 0xFF;
-            if (i == 0 || i > 0x7F) {
-                length = index;
-                break;
-            }
-        }
-
-        return new String(bytes, 0, length);
-    }
-
-    @Nullable
-    protected String getStringFromBytes(int tag, Charset cs)
-    {
-        byte[] values = _directory.getByteArray(tag);
-
-        if (values == null)
-            return null;
-
-        try {
-            return new String(values, cs.name()).trim();
-        } catch (UnsupportedEncodingException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    protected String getRationalOrDoubleString(int tagType)
-    {
-        Rational rational = _directory.getRational(tagType);
-        if (rational != null)
-            return rational.toSimpleString(true);
-
-        Double d = _directory.getDoubleObject(tagType);
-        if (d != null) {
-            DecimalFormat format = new DecimalFormat("0.###");
-            return format.format(d);
-        }
-
-        return null;
-    }
-
-    @Nullable
-    protected static String getFStopDescription(double fStop)
-    {
-        DecimalFormat format = new DecimalFormat("0.0");
-        format.setRoundingMode(RoundingMode.HALF_UP);
-        return "f/" + format.format(fStop);
-    }
-
-    @Nullable
-    protected static String getFocalLengthDescription(double mm)
-    {
-        DecimalFormat format = new DecimalFormat("0.#");
-        format.setRoundingMode(RoundingMode.HALF_UP);
-        return format.format(mm) + " mm";
-    }
-
-    @Nullable
-    protected String getLensSpecificationDescription(int tag)
-    {
-        Rational[] values = _directory.getRationalArray(tag);
-
-        if (values == null || values.length != 4 || (values[0].isZero() && values[2].isZero()))
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-
-        if (values[0].equals(values[1]))
-            sb.append(values[0].toSimpleString(true)).append("mm");
-        else
-            sb.append(values[0].toSimpleString(true)).append('-').append(values[1].toSimpleString(true)).append("mm");
-
-        if (!values[2].isZero()) {
-            sb.append(' ');
-
-            DecimalFormat format = new DecimalFormat("0.0");
-            format.setRoundingMode(RoundingMode.HALF_UP);
-
-            if (values[2].equals(values[3]))
-                sb.append(getFStopDescription(values[2].doubleValue()));
-            else
-                sb.append("f/").append(format.format(values[2].doubleValue())).append('-').append(format.format(values[3].doubleValue()));
-        }
-
-        return sb.toString();
-    }
-
-    @Nullable
-    protected String getOrientationDescription(int tag)
-    {
-        return getIndexedDescription(tag, 1,
-            "Top, left side (Horizontal / normal)",
-            "Top, right side (Mirror horizontal)",
-            "Bottom, right side (Rotate 180)",
-            "Bottom, left side (Mirror vertical)",
-            "Left side, top (Mirror horizontal and rotate 270 CW)",
-            "Right side, top (Rotate 90 CW)",
-            "Right side, bottom (Mirror horizontal and rotate 90 CW)",
-            "Left side, bottom (Rotate 270 CW)");
-    }
-
-    @Nullable
-    protected String getShutterSpeedDescription(int tag)
-    {
-        // I believe this method to now be stable, but am leaving some alternative snippets of
-        // code in here, to assist anyone who's looking into this (given that I don't have a public CVS).
-
-//        float apexValue = _directory.getFloat(ExifSubIFDDirectory.TAG_SHUTTER_SPEED);
-//        int apexPower = (int)Math.pow(2.0, apexValue);
-//        return "1/" + apexPower + " sec";
-        // TODO test this method
-        // thanks to Mark Edwards for spotting and patching a bug in the calculation of this
-        // description (spotted bug using a Canon EOS 300D)
-        // thanks also to Gli Blr for spotting this bug
-        Float apexValue = _directory.getFloatObject(tag);
-        if (apexValue == null)
-            return null;
-        if (apexValue <= 1) {
-            float apexPower = (float)(1 / (Math.exp(apexValue * Math.log(2))));
-            long apexPower10 = Math.round(apexPower * 10.0);
-            float fApexPower = apexPower10 / 10.0f;
-            DecimalFormat format = new DecimalFormat("0.##");
-            format.setRoundingMode(RoundingMode.HALF_UP);
-            return format.format(fApexPower) + " sec";
-        } else {
-            int apexPower = (int)((Math.exp(apexValue * Math.log(2))));
-            return "1/" + apexPower + " sec";
-        }
-
-/*
-        // This alternative implementation offered by Bill Richards
-        // TODO determine which is the correct / more-correct implementation
-        double apexValue = _directory.getDouble(ExifSubIFDDirectory.TAG_SHUTTER_SPEED);
-        double apexPower = Math.pow(2.0, apexValue);
-
-        StringBuffer sb = new StringBuffer();
-        if (apexPower > 1)
-            apexPower = Math.floor(apexPower);
-
-        if (apexPower < 1) {
-            sb.append((int)Math.round(1/apexPower));
-        } else {
-            sb.append("1/");
-            sb.append((int)apexPower);
-        }
-        sb.append(" sec");
-        return sb.toString();
-*/
-    }
-
-    // EXIF LightSource
-    @Nullable
-    protected String getLightSourceDescription(short wbtype)
-    {
-        switch (wbtype) {
-            case 0:
-                return "Unknown";
-            case 1:
-                return "Daylight";
-            case 2:
-                return "Fluorescent";
-            case 3:
-                return "Tungsten (Incandescent)";
-            case 4:
-                return "Flash";
-            case 9:
-                return "Fine Weather";
-            case 10:
-                return "Cloudy";
-            case 11:
-                return "Shade";
-            case 12:
-                return "Daylight Fluorescent";    // (D 5700 - 7100K)
-            case 13:
-                return "Day White Fluorescent";   // (N 4600 - 5500K)
-            case 14:
-                return "Cool White Fluorescent";  // (W 3800 - 4500K)
-            case 15:
-                return "White Fluorescent";       // (WW 3250 - 3800K)
-            case 16:
-                return "Warm White Fluorescent";  // (L 2600 - 3250K)
-            case 17:
-                return "Standard Light A";
-            case 18:
-                return "Standard Light B";
-            case 19:
-                return "Standard Light C";
-            case 20:
-                return "D55";
-            case 21:
-                return "D65";
-            case 22:
-                return "D75";
-            case 23:
-                return "D50";
-            case 24:
-                return "ISO Studio Tungsten";
-            case 255:
-                return "Other";
-        }
-
-        return getDescription(wbtype);
-    }
-
-    // EXIF UserComment, GPSProcessingMethod and GPSAreaInformation
-    @Nullable
-    protected String getEncodedTextDescription(int tagType)
-    {
-        byte[] commentBytes = _directory.getByteArray(tagType);
-        if (commentBytes == null)
-            return null;
-        if (commentBytes.length == 0)
-            return "";
-
-        final Map<String, String> encodingMap = new HashMap<>();
-        encodingMap.put("ASCII", System.getProperty("file.encoding")); // Someone suggested "ISO-8859-1".
-        encodingMap.put("UNICODE", "UTF-16LE");
-        encodingMap.put("JIS", "Shift-JIS"); // We assume this charset for now.  Another suggestion is "JIS".
-
-        try {
-            if (commentBytes.length >= 10) {
-                String firstTenBytesString = new String(commentBytes, 0, 10);
-
-                // try each encoding name
-                for (Map.Entry<String, String> pair : encodingMap.entrySet()) {
-                    String encodingName = pair.getKey();
-                    String charset = pair.getValue();
-                    if (firstTenBytesString.startsWith(encodingName)) {
-                        // skip any null or blank characters commonly present after the encoding name, up to a limit of 10 from the start
-                        for (int j = encodingName.length(); j < 10; j++) {
-                            byte b = commentBytes[j];
-                            if (b != '\0' && b != ' ')
-                                return new String(commentBytes, j, commentBytes.length - j, charset).trim();
-                        }
-                        return new String(commentBytes, 10, commentBytes.length - 10, charset).trim();
-                    }
-                }
-            }
-            // special handling fell through, return a plain string representation
-            return new String(commentBytes, System.getProperty("file.encoding")).trim();
-        } catch (UnsupportedEncodingException ex) {
-            return null;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifDescriptorBase.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifDescriptorBase.java	(revision 16024)
+++ 	(revision )
@@ -1,1298 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.imaging.PhotographicConversions;
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.lang.ByteArrayReader;
-import com.drew.metadata.Directory;
-import com.drew.metadata.TagDescriptor;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.text.DecimalFormat;
-
-import static com.drew.metadata.exif.ExifDirectoryBase.*;
-
-/**
- * Base class for several Exif format descriptor classes.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public abstract class ExifDescriptorBase<T extends Directory> extends TagDescriptor<T>
-{
-    /**
-     * Dictates whether rational values will be represented in decimal format in instances
-     * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).
-     */
-    private final boolean _allowDecimalRepresentationOfRationals = true;
-
-    // Note for the potential addition of brightness presentation in eV:
-    // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv),
-    // you must add SensitivityValue(Sv).
-    // Ev=BV+Sv   Sv=log2(ISOSpeedRating/3.125)
-    // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
-
-    public ExifDescriptorBase(@NotNull T directory)
-    {
-        super(directory);
-    }
-
-    @Nullable
-    @Override
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_INTEROP_INDEX:
-                return getInteropIndexDescription();
-            case TAG_INTEROP_VERSION:
-                return getInteropVersionDescription();
-            case TAG_NEW_SUBFILE_TYPE:
-                return getNewSubfileTypeDescription();
-            case TAG_SUBFILE_TYPE:
-                return getSubfileTypeDescription();
-            case TAG_IMAGE_WIDTH:
-                return getImageWidthDescription();
-            case TAG_IMAGE_HEIGHT:
-                return getImageHeightDescription();
-            case TAG_BITS_PER_SAMPLE:
-                return getBitsPerSampleDescription();
-            case TAG_COMPRESSION:
-                return getCompressionDescription();
-            case TAG_PHOTOMETRIC_INTERPRETATION:
-                return getPhotometricInterpretationDescription();
-            case TAG_THRESHOLDING:
-                return getThresholdingDescription();
-            case TAG_FILL_ORDER:
-                return getFillOrderDescription();
-            case TAG_ORIENTATION:
-                return getOrientationDescription();
-            case TAG_SAMPLES_PER_PIXEL:
-                return getSamplesPerPixelDescription();
-            case TAG_ROWS_PER_STRIP:
-                return getRowsPerStripDescription();
-            case TAG_STRIP_BYTE_COUNTS:
-                return getStripByteCountsDescription();
-            case TAG_X_RESOLUTION:
-                return getXResolutionDescription();
-            case TAG_Y_RESOLUTION:
-                return getYResolutionDescription();
-            case TAG_PLANAR_CONFIGURATION:
-                return getPlanarConfigurationDescription();
-            case TAG_RESOLUTION_UNIT:
-                return getResolutionDescription();
-            case TAG_JPEG_PROC:
-                return getJpegProcDescription();
-            case TAG_YCBCR_SUBSAMPLING:
-                return getYCbCrSubsamplingDescription();
-            case TAG_YCBCR_POSITIONING:
-                return getYCbCrPositioningDescription();
-            case TAG_REFERENCE_BLACK_WHITE:
-                return getReferenceBlackWhiteDescription();
-            case TAG_CFA_PATTERN_2:
-                return getCfaPattern2Description();
-            case TAG_EXPOSURE_TIME:
-                return getExposureTimeDescription();
-            case TAG_FNUMBER:
-                return getFNumberDescription();
-            case TAG_EXPOSURE_PROGRAM:
-                return getExposureProgramDescription();
-            case TAG_ISO_EQUIVALENT:
-                return getIsoEquivalentDescription();
-            case TAG_SENSITIVITY_TYPE:
-                return getSensitivityTypeRangeDescription();
-            case TAG_EXIF_VERSION:
-                return getExifVersionDescription();
-            case TAG_COMPONENTS_CONFIGURATION:
-                return getComponentConfigurationDescription();
-            case TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL:
-                return getCompressedAverageBitsPerPixelDescription();
-            case TAG_SHUTTER_SPEED:
-                return getShutterSpeedDescription();
-            case TAG_APERTURE:
-                return getApertureValueDescription();
-            case TAG_BRIGHTNESS_VALUE:
-                return getBrightnessValueDescription();
-            case TAG_EXPOSURE_BIAS:
-                return getExposureBiasDescription();
-            case TAG_MAX_APERTURE:
-                return getMaxApertureValueDescription();
-            case TAG_SUBJECT_DISTANCE:
-                return getSubjectDistanceDescription();
-            case TAG_METERING_MODE:
-                return getMeteringModeDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_FLASH:
-                return getFlashDescription();
-            case TAG_FOCAL_LENGTH:
-                return getFocalLengthDescription();
-            case TAG_USER_COMMENT:
-                return getUserCommentDescription();
-            case TAG_TEMPERATURE:
-                return getTemperatureDescription();
-            case TAG_HUMIDITY:
-                return getHumidityDescription();
-            case TAG_PRESSURE:
-                return getPressureDescription();
-            case TAG_WATER_DEPTH:
-                return getWaterDepthDescription();
-            case TAG_ACCELERATION:
-                return getAccelerationDescription();
-            case TAG_CAMERA_ELEVATION_ANGLE:
-                return getCameraElevationAngleDescription();
-            case TAG_WIN_TITLE:
-                return getWindowsTitleDescription();
-            case TAG_WIN_COMMENT:
-                return getWindowsCommentDescription();
-            case TAG_WIN_AUTHOR:
-                return getWindowsAuthorDescription();
-            case TAG_WIN_KEYWORDS:
-                return getWindowsKeywordsDescription();
-            case TAG_WIN_SUBJECT:
-                return getWindowsSubjectDescription();
-            case TAG_FLASHPIX_VERSION:
-                return getFlashPixVersionDescription();
-            case TAG_COLOR_SPACE:
-                return getColorSpaceDescription();
-            case TAG_EXIF_IMAGE_WIDTH:
-                return getExifImageWidthDescription();
-            case TAG_EXIF_IMAGE_HEIGHT:
-                return getExifImageHeightDescription();
-            case TAG_FOCAL_PLANE_X_RESOLUTION:
-                return getFocalPlaneXResolutionDescription();
-            case TAG_FOCAL_PLANE_Y_RESOLUTION:
-                return getFocalPlaneYResolutionDescription();
-            case TAG_FOCAL_PLANE_RESOLUTION_UNIT:
-                return getFocalPlaneResolutionUnitDescription();
-            case TAG_SENSING_METHOD:
-                return getSensingMethodDescription();
-            case TAG_FILE_SOURCE:
-                return getFileSourceDescription();
-            case TAG_SCENE_TYPE:
-                return getSceneTypeDescription();
-            case TAG_CFA_PATTERN:
-                return getCfaPatternDescription();
-            case TAG_CUSTOM_RENDERED:
-                return getCustomRenderedDescription();
-            case TAG_EXPOSURE_MODE:
-                return getExposureModeDescription();
-            case TAG_WHITE_BALANCE_MODE:
-                return getWhiteBalanceModeDescription();
-            case TAG_DIGITAL_ZOOM_RATIO:
-                return getDigitalZoomRatioDescription();
-            case TAG_35MM_FILM_EQUIV_FOCAL_LENGTH:
-                return get35mmFilmEquivFocalLengthDescription();
-            case TAG_SCENE_CAPTURE_TYPE:
-                return getSceneCaptureTypeDescription();
-            case TAG_GAIN_CONTROL:
-                return getGainControlDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_SATURATION:
-                return getSaturationDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case TAG_SUBJECT_DISTANCE_RANGE:
-                return getSubjectDistanceRangeDescription();
-            case TAG_LENS_SPECIFICATION:
-                return getLensSpecificationDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getInteropIndexDescription()
-    {
-        String value = _directory.getString(TAG_INTEROP_INDEX);
-
-        if (value == null)
-            return null;
-
-        return "R98".equalsIgnoreCase(value.trim())
-            ? "Recommended Exif Interoperability Rules (ExifR98)"
-            : "Unknown (" + value + ")";
-    }
-
-    @Nullable
-    public String getInteropVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_INTEROP_VERSION, 2);
-    }
-
-    @Nullable
-    public String getNewSubfileTypeDescription()
-    {
-        return getIndexedDescription(TAG_NEW_SUBFILE_TYPE, 0,
-            "Full-resolution image",
-            "Reduced-resolution image",
-            "Single page of multi-page image",
-            "Single page of multi-page reduced-resolution image",
-            "Transparency mask",
-            "Transparency mask of reduced-resolution image",
-            "Transparency mask of multi-page image",
-            "Transparency mask of reduced-resolution multi-page image"
-        );
-    }
-
-    @Nullable
-    public String getSubfileTypeDescription()
-    {
-        return getIndexedDescription(TAG_SUBFILE_TYPE, 1,
-            "Full-resolution image",
-            "Reduced-resolution image",
-            "Single page of multi-page image"
-        );
-    }
-
-    @Nullable
-    public String getImageWidthDescription()
-    {
-        String value = _directory.getString(TAG_IMAGE_WIDTH);
-        return value == null ? null : value + " pixels";
-    }
-
-    @Nullable
-    public String getImageHeightDescription()
-    {
-        String value = _directory.getString(TAG_IMAGE_HEIGHT);
-        return value == null ? null : value + " pixels";
-    }
-
-    @Nullable
-    public String getBitsPerSampleDescription()
-    {
-        String value = _directory.getString(TAG_BITS_PER_SAMPLE);
-        return value == null ? null : value + " bits/component/pixel";
-    }
-
-    @Nullable
-    public String getCompressionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_COMPRESSION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 1: return "Uncompressed";
-            case 2: return "CCITT 1D";
-            case 3: return "T4/Group 3 Fax";
-            case 4: return "T6/Group 4 Fax";
-            case 5: return "LZW";
-            case 6: return "JPEG (old-style)";
-            case 7: return "JPEG";
-            case 8: return "Adobe Deflate";
-            case 9: return "JBIG B&W";
-            case 10: return "JBIG Color";
-            case 99: return "JPEG";
-            case 262: return "Kodak 262";
-            case 32766: return "Next";
-            case 32767: return "Sony ARW Compressed";
-            case 32769: return "Packed RAW";
-            case 32770: return "Samsung SRW Compressed";
-            case 32771: return "CCIRLEW";
-            case 32772: return "Samsung SRW Compressed 2";
-            case 32773: return "PackBits";
-            case 32809: return "Thunderscan";
-            case 32867: return "Kodak KDC Compressed";
-            case 32895: return "IT8CTPAD";
-            case 32896: return "IT8LW";
-            case 32897: return "IT8MP";
-            case 32898: return "IT8BL";
-            case 32908: return "PixarFilm";
-            case 32909: return "PixarLog";
-            case 32946: return "Deflate";
-            case 32947: return "DCS";
-            case 34661: return "JBIG";
-            case 34676: return "SGILog";
-            case 34677: return "SGILog24";
-            case 34712: return "JPEG 2000";
-            case 34713: return "Nikon NEF Compressed";
-            case 34715: return "JBIG2 TIFF FX";
-            case 34718: return "Microsoft Document Imaging (MDI) Binary Level Codec";
-            case 34719: return "Microsoft Document Imaging (MDI) Progressive Transform Codec";
-            case 34720: return "Microsoft Document Imaging (MDI) Vector";
-            case 34892: return "Lossy JPEG";
-            case 65000: return "Kodak DCR Compressed";
-            case 65535: return "Pentax PEF Compressed";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getPhotometricInterpretationDescription()
-    {
-        // Shows the color space of the image data components
-        Integer value = _directory.getInteger(TAG_PHOTOMETRIC_INTERPRETATION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "WhiteIsZero";
-            case 1: return "BlackIsZero";
-            case 2: return "RGB";
-            case 3: return "RGB Palette";
-            case 4: return "Transparency Mask";
-            case 5: return "CMYK";
-            case 6: return "YCbCr";
-            case 8: return "CIELab";
-            case 9: return "ICCLab";
-            case 10: return "ITULab";
-            case 32803: return "Color Filter Array";
-            case 32844: return "Pixar LogL";
-            case 32845: return "Pixar LogLuv";
-            case 32892: return "Linear Raw";
-            default:
-                return "Unknown colour space";
-        }
-    }
-
-    @Nullable
-    public String getThresholdingDescription()
-    {
-        return getIndexedDescription(TAG_THRESHOLDING, 1,
-            "No dithering or halftoning",
-            "Ordered dither or halftone",
-            "Randomized dither"
-        );
-    }
-
-    @Nullable
-    public String getFillOrderDescription()
-    {
-        return getIndexedDescription(TAG_FILL_ORDER, 1,
-            "Normal",
-            "Reversed"
-        );
-    }
-
-    @Nullable
-    public String getOrientationDescription()
-    {
-        return super.getOrientationDescription(TAG_ORIENTATION);
-    }
-
-    @Nullable
-    public String getSamplesPerPixelDescription()
-    {
-        String value = _directory.getString(TAG_SAMPLES_PER_PIXEL);
-        return value == null ? null : value + " samples/pixel";
-    }
-
-    @Nullable
-    public String getRowsPerStripDescription()
-    {
-        final String value = _directory.getString(TAG_ROWS_PER_STRIP);
-        return value == null ? null : value + " rows/strip";
-    }
-
-    @Nullable
-    public String getStripByteCountsDescription()
-    {
-        final String value = _directory.getString(TAG_STRIP_BYTE_COUNTS);
-        return value == null ? null : value + " bytes";
-    }
-
-    @Nullable
-    public String getXResolutionDescription()
-    {
-        Rational value = _directory.getRational(TAG_X_RESOLUTION);
-        if (value == null)
-            return null;
-        final String unit = getResolutionDescription();
-        return String.format("%s dots per %s",
-            value.toSimpleString(_allowDecimalRepresentationOfRationals),
-            unit == null ? "unit" : unit.toLowerCase());
-    }
-
-    @Nullable
-    public String getYResolutionDescription()
-    {
-        Rational value = _directory.getRational(TAG_Y_RESOLUTION);
-        if (value==null)
-            return null;
-        final String unit = getResolutionDescription();
-        return String.format("%s dots per %s",
-            value.toSimpleString(_allowDecimalRepresentationOfRationals),
-            unit == null ? "unit" : unit.toLowerCase());
-    }
-
-    @Nullable
-    public String getPlanarConfigurationDescription()
-    {
-        // When image format is no compression YCbCr, this value shows byte aligns of YCbCr
-        // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling
-        // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr
-        // plane format.
-        return getIndexedDescription(TAG_PLANAR_CONFIGURATION,
-            1,
-            "Chunky (contiguous for each subsampling pixel)",
-            "Separate (Y-plane/Cb-plane/Cr-plane format)"
-        );
-    }
-
-    @Nullable
-    public String getResolutionDescription()
-    {
-        // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)
-        return getIndexedDescription(TAG_RESOLUTION_UNIT, 1, "(No unit)", "Inch", "cm");
-    }
-
-    @Nullable
-    public String getJpegProcDescription()
-    {
-        Integer value = _directory.getInteger(TAG_JPEG_PROC);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 1: return "Baseline";
-            case 14: return "Lossless";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getYCbCrSubsamplingDescription()
-    {
-        int[] positions = _directory.getIntArray(TAG_YCBCR_SUBSAMPLING);
-        if (positions == null || positions.length < 2)
-            return null;
-        if (positions[0] == 2 && positions[1] == 1) {
-            return "YCbCr4:2:2";
-        } else if (positions[0] == 2 && positions[1] == 2) {
-            return "YCbCr4:2:0";
-        } else {
-            return "(Unknown)";
-        }
-    }
-
-    @Nullable
-    public String getYCbCrPositioningDescription()
-    {
-        return getIndexedDescription(TAG_YCBCR_POSITIONING, 1, "Center of pixel array", "Datum point");
-    }
-
-    @Nullable
-    public String getReferenceBlackWhiteDescription()
-    {
-        // For some reason, sometimes this is read as a long[] and
-        // getIntArray isn't able to deal with it
-        int[] ints = _directory.getIntArray(TAG_REFERENCE_BLACK_WHITE);
-        if (ints==null || ints.length < 6)
-        {
-            Object o = _directory.getObject(TAG_REFERENCE_BLACK_WHITE);
-            if (o != null && (o instanceof long[]))
-            {
-                long[] longs = (long[])o;
-                if (longs.length < 6)
-                    return null;
-
-                ints = new int[longs.length];
-                for (int i = 0; i < longs.length; i++)
-                    ints[i] = (int)longs[i];
-            }
-            else
-                return null;
-        }
-
-        int blackR = ints[0];
-        int whiteR = ints[1];
-        int blackG = ints[2];
-        int whiteG = ints[3];
-        int blackB = ints[4];
-        int whiteB = ints[5];
-        return String.format("[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB);
-    }
-
-    /// <summary>
-    /// String description of CFA Pattern
-    /// </summary>
-    /// <remarks>
-    /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used.
-    /// It does not apply to all sensing methods.
-    ///
-    /// ExifDirectoryBase.TAG_CFA_PATTERN_2 holds only the pixel pattern. ExifDirectoryBase.TAG_CFA_REPEAT_PATTERN_DIM is expected to exist and pass
-    /// some conditional tests.
-    /// </remarks>
-    @Nullable
-    public String getCfaPattern2Description()
-    {
-        byte[] values = _directory.getByteArray(TAG_CFA_PATTERN_2);
-        if (values == null)
-            return null;
-
-        int[] repeatPattern = _directory.getIntArray(TAG_CFA_REPEAT_PATTERN_DIM);
-        if (repeatPattern == null)
-            return String.format("Repeat Pattern not found for CFAPattern (%s)", super.getDescription(TAG_CFA_PATTERN_2));
-
-        if (repeatPattern.length == 2 && values.length == (repeatPattern[0] * repeatPattern[1]))
-        {
-            int[] intpattern = new int[2 + values.length];
-            intpattern[0] = repeatPattern[0];
-            intpattern[1] = repeatPattern[1];
-
-            for (int i = 0; i < values.length; i++)
-                intpattern[i + 2] = values[i] & 0xFF;   // convert the values[i] byte to unsigned
-
-            return formatCFAPattern(intpattern);
-        }
-
-        return String.format("Unknown Pattern (%s)", super.getDescription(TAG_CFA_PATTERN_2));
-    }
-
-    @Nullable
-    private static String formatCFAPattern(@Nullable int[] pattern)
-    {
-        if (pattern == null)
-            return null;
-        if (pattern.length < 2)
-            return "<truncated data>";
-        if (pattern[0] == 0 && pattern[1] == 0)
-            return "<zero pattern size>";
-
-        int end = 2 + pattern[0] * pattern[1];
-        if (end > pattern.length)
-            return "<invalid pattern size>";
-
-        String[] cfaColors = { "Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "White" };
-
-        StringBuilder ret = new StringBuilder();
-        ret.append("[");
-        for (int pos = 2; pos < end; pos++)
-        {
-            if (pattern[pos] <= cfaColors.length - 1)
-                ret.append(cfaColors[pattern[pos]]);
-            else
-                ret.append("Unknown");      // indicated pattern position is outside the array bounds
-
-            if ((pos - 2) % pattern[1] == 0)
-                ret.append(",");
-            else if(pos != end - 1)
-                ret.append("][");
-        }
-        ret.append("]");
-
-        return ret.toString();
-    }
-
-    @Nullable
-    public String getExposureTimeDescription()
-    {
-        String value = _directory.getString(TAG_EXPOSURE_TIME);
-        return value == null ? null : value + " sec";
-    }
-
-    @Nullable
-    public String getFNumberDescription()
-    {
-        Rational value = _directory.getRational(TAG_FNUMBER);
-        if (value == null)
-            return null;
-        return getFStopDescription(value.doubleValue());
-    }
-
-    @Nullable
-    public String getExposureProgramDescription()
-    {
-        return getIndexedDescription(TAG_EXPOSURE_PROGRAM,
-            1,
-            "Manual control",
-            "Program normal",
-            "Aperture priority",
-            "Shutter priority",
-            "Program creative (slow program)",
-            "Program action (high-speed program)",
-            "Portrait mode",
-            "Landscape mode"
-        );
-    }
-
-    @Nullable
-    public String getIsoEquivalentDescription()
-    {
-        // Have seen an exception here from files produced by ACDSEE that stored an int[] here with two values
-        Integer isoEquiv = _directory.getInteger(TAG_ISO_EQUIVALENT);
-        // There used to be a check here that multiplied ISO values < 50 by 200.
-        // Issue 36 shows a smart-phone image from a Samsung Galaxy S2 with ISO-40.
-        return isoEquiv != null
-            ? Integer.toString(isoEquiv)
-            : null;
-    }
-
-    @Nullable
-    public String getSensitivityTypeRangeDescription()
-    {
-        return getIndexedDescription(TAG_SENSITIVITY_TYPE,
-            "Unknown",
-            "Standard Output Sensitivity",
-            "Recommended Exposure Index",
-            "ISO Speed",
-            "Standard Output Sensitivity and Recommended Exposure Index",
-            "Standard Output Sensitivity and ISO Speed",
-            "Recommended Exposure Index and ISO Speed",
-            "Standard Output Sensitivity, Recommended Exposure Index and ISO Speed"
-        );
-    }
-
-    @Nullable
-    public String getExifVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_EXIF_VERSION, 2);
-    }
-
-    @Nullable
-    public String getComponentConfigurationDescription()
-    {
-        int[] components = _directory.getIntArray(TAG_COMPONENTS_CONFIGURATION);
-        if (components == null)
-            return null;
-        String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"};
-        StringBuilder componentConfig = new StringBuilder();
-        for (int i = 0; i < Math.min(4, components.length); i++) {
-            int j = components[i];
-            if (j > 0 && j < componentStrings.length) {
-                componentConfig.append(componentStrings[j]);
-            }
-        }
-        return componentConfig.toString();
-    }
-
-    @Nullable
-    public String getCompressedAverageBitsPerPixelDescription()
-    {
-        Rational value = _directory.getRational(TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL);
-        if (value == null)
-            return null;
-        String ratio = value.toSimpleString(_allowDecimalRepresentationOfRationals);
-        return value.isInteger() && value.intValue() == 1
-            ? ratio + " bit/pixel"
-            : ratio + " bits/pixel";
-    }
-
-    @Nullable
-    public String getShutterSpeedDescription()
-    {
-        return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED);
-    }
-
-    @Nullable
-    public String getApertureValueDescription()
-    {
-        Double aperture = _directory.getDoubleObject(TAG_APERTURE);
-        if (aperture == null)
-            return null;
-        double fStop = PhotographicConversions.apertureToFStop(aperture);
-        return getFStopDescription(fStop);
-    }
-
-    @Nullable
-    public String getBrightnessValueDescription()
-    {
-        Rational value = _directory.getRational(TAG_BRIGHTNESS_VALUE);
-        if (value == null)
-            return null;
-        if (value.getNumerator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0##");
-        return formatter.format(value.doubleValue());
-    }
-
-    @Nullable
-    public String getExposureBiasDescription()
-    {
-        Rational value = _directory.getRational(TAG_EXPOSURE_BIAS);
-        if (value == null)
-            return null;
-        return value.toSimpleString(true) + " EV";
-    }
-
-    @Nullable
-    public String getMaxApertureValueDescription()
-    {
-        Double aperture = _directory.getDoubleObject(TAG_MAX_APERTURE);
-        if (aperture == null)
-            return null;
-        double fStop = PhotographicConversions.apertureToFStop(aperture);
-        return getFStopDescription(fStop);
-    }
-
-    @Nullable
-    public String getSubjectDistanceDescription()
-    {
-        Rational value = _directory.getRational(TAG_SUBJECT_DISTANCE);
-        if (value == null)
-            return null;
-        if (value.getNumerator() == 0xFFFFFFFFL)
-            return "Infinity";
-        if (value.getNumerator() == 0)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0##");
-        return formatter.format(value.doubleValue()) + " metres";
-    }
-
-    @Nullable
-    public String getMeteringModeDescription()
-    {
-        // '0' means unknown, '1' average, '2' center weighted average, '3' spot
-        // '4' multi-spot, '5' multi-segment, '6' partial, '255' other
-        Integer value = _directory.getInteger(TAG_METERING_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Unknown";
-            case 1: return "Average";
-            case 2: return "Center weighted average";
-            case 3: return "Spot";
-            case 4: return "Multi-spot";
-            case 5: return "Multi-segment";
-            case 6: return "Partial";
-            case 255: return "(Other)";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        // See http://web.archive.org/web/20131018091152/http://exif.org/Exif2-2.PDF page 35
-        final Integer value = _directory.getInteger(TAG_WHITE_BALANCE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Unknown";
-            case 1: return "Daylight";
-            case 2: return "Florescent";
-            case 3: return "Tungsten";
-            case 4: return "Flash";
-            case 9: return "Fine Weather";
-            case 10: return "Cloudy";
-            case 11: return "Shade";
-            case 12: return "Daylight Fluorescent";
-            case 13: return "Day White Fluorescent";
-            case 14: return "Cool White Fluorescent";
-            case 15: return "White Fluorescent";
-            case 16: return "Warm White Fluorescent";
-            case 17: return "Standard light";
-            case 18: return "Standard light (B)";
-            case 19: return "Standard light (C)";
-            case 20: return "D55";
-            case 21: return "D65";
-            case 22: return "D75";
-            case 23: return "D50";
-            case 24: return "Studio Tungsten";
-            case 255: return "(Other)";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFlashDescription()
-    {
-        /*
-         * This is a bit mask.
-         * 0 = flash fired
-         * 1 = return detected
-         * 2 = return able to be detected
-         * 3 = unknown
-         * 4 = auto used
-         * 5 = unknown
-         * 6 = red eye reduction used
-         */
-
-        final Integer value = _directory.getInteger(TAG_FLASH);
-
-        if (value == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-
-        if ((value & 0x1) != 0)
-            sb.append("Flash fired");
-        else
-            sb.append("Flash did not fire");
-
-        // check if we're able to detect a return, before we mention it
-        if ((value & 0x4) != 0) {
-            if ((value & 0x2) != 0)
-                sb.append(", return detected");
-            else
-                sb.append(", return not detected");
-        }
-
-        // If 0x10 is set and the lowest byte is not zero - then flash is Auto
-        if ((value & 0x10) != 0 && (value & 0x0F) != 0)
-            sb.append(", auto");
-
-        if ((value & 0x40) != 0)
-            sb.append(", red-eye reduction");
-
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getFocalLengthDescription()
-    {
-        Rational value = _directory.getRational(TAG_FOCAL_LENGTH);
-        return value == null ? null : getFocalLengthDescription(value.doubleValue());
-    }
-
-    @Nullable
-    public String getUserCommentDescription()
-    {
-        return getEncodedTextDescription(TAG_USER_COMMENT);
-    }
-
-    @Nullable
-    public String getTemperatureDescription()
-    {
-        Rational value = _directory.getRational(TAG_TEMPERATURE);
-        if (value == null)
-            return null;
-        if (value.getDenominator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0");
-        return formatter.format(value.doubleValue()) + " °C";
-    }
-
-    @Nullable
-    public String getHumidityDescription()
-    {
-        Rational value = _directory.getRational(TAG_HUMIDITY);
-        if (value == null)
-            return null;
-        if (value.getDenominator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0");
-        return formatter.format(value.doubleValue()) + " %";
-    }
-
-    @Nullable
-    public String getPressureDescription()
-    {
-        Rational value = _directory.getRational(TAG_PRESSURE);
-        if (value == null)
-            return null;
-        if (value.getDenominator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0");
-        return formatter.format(value.doubleValue()) + " hPa";
-    }
-
-    @Nullable
-    public String getWaterDepthDescription()
-    {
-        Rational value = _directory.getRational(TAG_WATER_DEPTH);
-        if (value == null)
-            return null;
-        if (value.getDenominator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0##");
-        return formatter.format(value.doubleValue()) + " metres";
-    }
-
-    @Nullable
-    public String getAccelerationDescription()
-    {
-        Rational value = _directory.getRational(TAG_ACCELERATION);
-        if (value == null)
-            return null;
-        if (value.getDenominator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.0##");
-        return formatter.format(value.doubleValue()) + " mGal";
-    }
-
-    @Nullable
-    public String getCameraElevationAngleDescription()
-    {
-        Rational value = _directory.getRational(TAG_CAMERA_ELEVATION_ANGLE);
-        if (value == null)
-            return null;
-        if (value.getDenominator() == 0xFFFFFFFFL)
-            return "Unknown";
-        DecimalFormat formatter = new DecimalFormat("0.##");
-        return formatter.format(value.doubleValue()) + " degrees";
-    }
-
-    /** The Windows specific tags uses plain Unicode. */
-    @Nullable
-    private String getUnicodeDescription(int tag)
-    {
-        byte[] bytes = _directory.getByteArray(tag);
-        if (bytes == null)
-            return null;
-        try {
-            // Decode the unicode string and trim the unicode zero "\0" from the end.
-            return new String(bytes, "UTF-16LE").trim();
-        } catch (UnsupportedEncodingException ex) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getWindowsTitleDescription()
-    {
-        return getUnicodeDescription(TAG_WIN_TITLE);
-    }
-
-    @Nullable
-    public String getWindowsCommentDescription()
-    {
-        return getUnicodeDescription(TAG_WIN_COMMENT);
-    }
-
-    @Nullable
-    public String getWindowsAuthorDescription()
-    {
-        return getUnicodeDescription(TAG_WIN_AUTHOR);
-    }
-
-    @Nullable
-    public String getWindowsKeywordsDescription()
-    {
-        return getUnicodeDescription(TAG_WIN_KEYWORDS);
-    }
-
-    @Nullable
-    public String getWindowsSubjectDescription()
-    {
-        return getUnicodeDescription(TAG_WIN_SUBJECT);
-    }
-
-    @Nullable
-    public String getFlashPixVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_FLASHPIX_VERSION, 2);
-    }
-
-    @Nullable
-    public String getColorSpaceDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_COLOR_SPACE);
-        if (value == null)
-            return null;
-        if (value == 1)
-            return "sRGB";
-        if (value == 65535)
-            return "Undefined";
-        return "Unknown (" + value + ")";
-    }
-
-    @Nullable
-    public String getExifImageWidthDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_EXIF_IMAGE_WIDTH);
-        return value == null ? null : value + " pixels";
-    }
-
-    @Nullable
-    public String getExifImageHeightDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_EXIF_IMAGE_HEIGHT);
-        return value == null ? null : value + " pixels";
-    }
-
-    @Nullable
-    public String getFocalPlaneXResolutionDescription()
-    {
-        Rational rational = _directory.getRational(TAG_FOCAL_PLANE_X_RESOLUTION);
-        if (rational == null)
-            return null;
-        final String unit = getFocalPlaneResolutionUnitDescription();
-        return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals)
-            + (unit == null ? "" : " " + unit.toLowerCase());
-    }
-
-    @Nullable
-    public String getFocalPlaneYResolutionDescription()
-    {
-        Rational rational = _directory.getRational(TAG_FOCAL_PLANE_Y_RESOLUTION);
-        if (rational == null)
-            return null;
-        final String unit = getFocalPlaneResolutionUnitDescription();
-        return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals)
-            + (unit == null ? "" : " " + unit.toLowerCase());
-    }
-
-    @Nullable
-    public String getFocalPlaneResolutionUnitDescription()
-    {
-        // Unit of FocalPlaneXResolution/FocalPlaneYResolution.
-        // '1' means no-unit, '2' inch, '3' centimeter.
-        return getIndexedDescription(TAG_FOCAL_PLANE_RESOLUTION_UNIT,
-            1,
-            "(No unit)",
-            "Inches",
-            "cm"
-        );
-    }
-
-    @Nullable
-    public String getSensingMethodDescription()
-    {
-        // '1' Not defined, '2' One-chip color area sensor, '3' Two-chip color area sensor
-        // '4' Three-chip color area sensor, '5' Color sequential area sensor
-        // '7' Trilinear sensor '8' Color sequential linear sensor,  'Other' reserved
-        return getIndexedDescription(TAG_SENSING_METHOD,
-            1,
-            "(Not defined)",
-            "One-chip color area sensor",
-            "Two-chip color area sensor",
-            "Three-chip color area sensor",
-            "Color sequential area sensor",
-            null,
-            "Trilinear sensor",
-            "Color sequential linear sensor"
-        );
-    }
-
-    @Nullable
-    public String getFileSourceDescription()
-    {
-        return getIndexedDescription(TAG_FILE_SOURCE,
-            1,
-            "Film Scanner",
-            "Reflection Print Scanner",
-            "Digital Still Camera (DSC)"
-        );
-    }
-
-    @Nullable
-    public String getSceneTypeDescription()
-    {
-        return getIndexedDescription(TAG_SCENE_TYPE,
-            1,
-            "Directly photographed image"
-        );
-    }
-
-    /// <summary>
-    /// String description of CFA Pattern
-    /// </summary>
-    /// <remarks>
-    /// Converted from Exiftool version 10.33 created by Phil Harvey
-    /// http://www.sno.phy.queensu.ca/~phil/exiftool/
-    /// lib\Image\ExifTool\Exif.pm
-    ///
-    /// Indicates the color filter array (CFA) geometric pattern of the image sensor when a one-chip color area sensor is used.
-    /// It does not apply to all sensing methods.
-    /// </remarks>
-    @Nullable
-    public String getCfaPatternDescription()
-    {
-        return formatCFAPattern(decodeCfaPattern(TAG_CFA_PATTERN));
-    }
-
-    /// <summary>
-    /// Decode raw CFAPattern value
-    /// </summary>
-    /// <remarks>
-    /// Converted from Exiftool version 10.33 created by Phil Harvey
-    /// http://www.sno.phy.queensu.ca/~phil/exiftool/
-    /// lib\Image\ExifTool\Exif.pm
-    ///
-    /// The value consists of:
-    /// - Two short, being the grid width and height of the repeated pattern.
-    /// - Next, for every pixel in that pattern, an identification code.
-    /// </remarks>
-    @Nullable
-    private int[] decodeCfaPattern(int tagType)
-    {
-        int[] ret;
-
-        byte[] values = _directory.getByteArray(tagType);
-        if (values == null)
-            return null;
-
-        if (values.length < 4)
-        {
-            ret = new int[values.length];
-            for (int i = 0; i < values.length; i++)
-                ret[i] = values[i];
-            return ret;
-        }
-
-        ret = new int[values.length - 2];
-
-        try {
-            ByteArrayReader reader = new ByteArrayReader(values);
-
-            // first two values should be read as 16-bits (2 bytes)
-            short item0 = reader.getInt16(0);
-            short item1 = reader.getInt16(2);
-
-            Boolean copyArray = false;
-            int end = 2 + item0 * item1;
-            if (end > values.length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values
-            {
-                // try swapping byte order (I have seen this order different than in EXIF)
-                reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder());
-                item0 = reader.getInt16(0);
-                item1 = reader.getInt16(2);
-
-                if (values.length >= (2 + item0 * item1))
-                    copyArray = true;
-            }
-            else
-                copyArray = true;
-
-            if(copyArray)
-            {
-                ret[0] = item0;
-                ret[1] = item1;
-
-                for (int i = 4; i < values.length; i++)
-                    ret[i - 2] = reader.getInt8(i);
-            }
-        } catch (IOException ex) {
-            _directory.addError("IO exception processing data: " + ex.getMessage());
-        }
-
-        return ret;
-    }
-
-    @Nullable
-    public String getCustomRenderedDescription()
-    {
-        return getIndexedDescription(TAG_CUSTOM_RENDERED,
-            "Normal process",
-            "Custom process"
-        );
-    }
-
-    @Nullable
-    public String getExposureModeDescription()
-    {
-        return getIndexedDescription(TAG_EXPOSURE_MODE,
-            "Auto exposure",
-            "Manual exposure",
-            "Auto bracket"
-        );
-    }
-
-    @Nullable
-    public String getWhiteBalanceModeDescription()
-    {
-        return getIndexedDescription(TAG_WHITE_BALANCE_MODE,
-            "Auto white balance",
-            "Manual white balance"
-        );
-    }
-
-    @Nullable
-    public String getDigitalZoomRatioDescription()
-    {
-        Rational value = _directory.getRational(TAG_DIGITAL_ZOOM_RATIO);
-        return value == null
-            ? null
-            : value.getNumerator() == 0
-                ? "Digital zoom not used"
-                : new DecimalFormat("0.#").format(value.doubleValue());
-    }
-
-    @Nullable
-    public String get35mmFilmEquivFocalLengthDescription()
-    {
-        Integer value = _directory.getInteger(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH);
-        return value == null
-            ? null
-            : value == 0
-                ? "Unknown"
-                : getFocalLengthDescription(value);
-    }
-
-    @Nullable
-    public String getSceneCaptureTypeDescription()
-    {
-        return getIndexedDescription(TAG_SCENE_CAPTURE_TYPE,
-            "Standard",
-            "Landscape",
-            "Portrait",
-            "Night scene"
-        );
-    }
-
-    @Nullable
-    public String getGainControlDescription()
-    {
-        return getIndexedDescription(TAG_GAIN_CONTROL,
-            "None",
-            "Low gain up",
-            "Low gain down",
-            "High gain up",
-            "High gain down"
-        );
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        return getIndexedDescription(TAG_CONTRAST,
-            "None",
-            "Soft",
-            "Hard"
-        );
-    }
-
-    @Nullable
-    public String getSaturationDescription()
-    {
-        return getIndexedDescription(TAG_SATURATION,
-            "None",
-            "Low saturation",
-            "High saturation"
-        );
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        return getIndexedDescription(TAG_SHARPNESS,
-            "None",
-            "Low",
-            "Hard"
-        );
-    }
-
-    @Nullable
-    public String getSubjectDistanceRangeDescription()
-    {
-        return getIndexedDescription(TAG_SUBJECT_DISTANCE_RANGE,
-            "Unknown",
-            "Macro",
-            "Close view",
-            "Distant view"
-        );
-    }
-
-    @Nullable
-    public String getLensSpecificationDescription()
-    {
-        return getLensSpecificationDescription(TAG_LENS_SPECIFICATION);
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifDirectoryBase.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifDirectoryBase.java	(revision 16024)
+++ 	(revision )
@@ -1,786 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Base class for several Exif format tag directories.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public abstract class ExifDirectoryBase extends Directory
-{
-    public static final int TAG_INTEROP_INDEX = 0x0001;
-    public static final int TAG_INTEROP_VERSION = 0x0002;
-
-    /**
-     * The new subfile type tag.
-     * 0 = Full-resolution Image
-     * 1 = Reduced-resolution image
-     * 2 = Single page of multi-page image
-     * 3 = Single page of multi-page reduced-resolution image
-     * 4 = Transparency mask
-     * 5 = Transparency mask of reduced-resolution image
-     * 6 = Transparency mask of multi-page image
-     * 7 = Transparency mask of reduced-resolution multi-page image
-     */
-    public static final int TAG_NEW_SUBFILE_TYPE                  = 0x00FE;
-    /**
-     * The old subfile type tag.
-     * 1 = Full-resolution image (Main image)
-     * 2 = Reduced-resolution image (Thumbnail)
-     * 3 = Single page of multi-page image
-     */
-    public static final int TAG_SUBFILE_TYPE                      = 0x00FF;
-
-    public static final int TAG_IMAGE_WIDTH                       = 0x0100;
-    public static final int TAG_IMAGE_HEIGHT                      = 0x0101;
-
-    /**
-     * When image format is no compression, this value shows the number of bits
-     * per component for each pixel. Usually this value is '8,8,8'.
-     */
-    public static final int TAG_BITS_PER_SAMPLE                   = 0x0102;
-    public static final int TAG_COMPRESSION                       = 0x0103;
-
-    /**
-     * Shows the color space of the image data components.
-     * 0 = WhiteIsZero
-     * 1 = BlackIsZero
-     * 2 = RGB
-     * 3 = RGB Palette
-     * 4 = Transparency Mask
-     * 5 = CMYK
-     * 6 = YCbCr
-     * 8 = CIELab
-     * 9 = ICCLab
-     * 10 = ITULab
-     * 32803 = Color Filter Array
-     * 32844 = Pixar LogL
-     * 32845 = Pixar LogLuv
-     * 34892 = Linear Raw
-     */
-    public static final int TAG_PHOTOMETRIC_INTERPRETATION        = 0x0106;
-
-    /**
-     * 1 = No dithering or halftoning
-     * 2 = Ordered dither or halftone
-     * 3 = Randomized dither
-     */
-    public static final int TAG_THRESHOLDING                      = 0x0107;
-
-    /**
-     * 1 = Normal
-     * 2 = Reversed
-     */
-    public static final int TAG_FILL_ORDER                        = 0x010A;
-    public static final int TAG_DOCUMENT_NAME                     = 0x010D;
-
-    public static final int TAG_IMAGE_DESCRIPTION                 = 0x010E;
-
-    public static final int TAG_MAKE                              = 0x010F;
-    public static final int TAG_MODEL                             = 0x0110;
-    /** The position in the file of raster data. */
-    public static final int TAG_STRIP_OFFSETS                     = 0x0111;
-    public static final int TAG_ORIENTATION                       = 0x0112;
-    /** Each pixel is composed of this many samples. */
-    public static final int TAG_SAMPLES_PER_PIXEL                 = 0x0115;
-    /** The raster is codified by a single block of data holding this many rows. */
-    public static final int TAG_ROWS_PER_STRIP                    = 0x0116;
-    /** The size of the raster data in bytes. */
-    public static final int TAG_STRIP_BYTE_COUNTS                 = 0x0117;
-    public static final int TAG_MIN_SAMPLE_VALUE                  = 0x0118;
-    public static final int TAG_MAX_SAMPLE_VALUE                  = 0x0119;
-    public static final int TAG_X_RESOLUTION                      = 0x011A;
-    public static final int TAG_Y_RESOLUTION                      = 0x011B;
-    /**
-     * When image format is no compression YCbCr, this value shows byte aligns of
-     * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for
-     * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and
-     * stored to Y plane/Cb plane/Cr plane format.
-     */
-    public static final int TAG_PLANAR_CONFIGURATION              = 0x011C;
-    public static final int TAG_PAGE_NAME                         = 0x011D;
-
-    public static final int TAG_RESOLUTION_UNIT                   = 0x0128;
-    public static final int TAG_PAGE_NUMBER                       = 0x0129;
-
-    public static final int TAG_TRANSFER_FUNCTION                 = 0x012D;
-    public static final int TAG_SOFTWARE                          = 0x0131;
-    public static final int TAG_DATETIME                          = 0x0132;
-    public static final int TAG_ARTIST                            = 0x013B;
-    public static final int TAG_HOST_COMPUTER                     = 0x013C;
-    public static final int TAG_PREDICTOR                         = 0x013D;
-    public static final int TAG_WHITE_POINT                       = 0x013E;
-    public static final int TAG_PRIMARY_CHROMATICITIES            = 0x013F;
-
-    public static final int TAG_TILE_WIDTH                        = 0x0142;
-    public static final int TAG_TILE_LENGTH                       = 0x0143;
-    public static final int TAG_TILE_OFFSETS                      = 0x0144;
-    public static final int TAG_TILE_BYTE_COUNTS                  = 0x0145;
-
-    /**
-     * Tag is a pointer to one or more sub-IFDs.
-     + Seems to be used exclusively by raw formats, referencing one or two IFDs.
-     */
-    public static final int TAG_SUB_IFD_OFFSET                    = 0x014a;
-
-    public static final int TAG_TRANSFER_RANGE                    = 0x0156;
-    public static final int TAG_JPEG_TABLES                       = 0x015B;
-    public static final int TAG_JPEG_PROC                         = 0x0200;
-
-    // 0x0201 can have all kinds of descriptions for thumbnail starting index
-    // 0x0202 can have all kinds of descriptions for thumbnail length
-    public static final int TAG_JPEG_RESTART_INTERVAL = 0x0203;
-    public static final int TAG_JPEG_LOSSLESS_PREDICTORS = 0x0205;
-    public static final int TAG_JPEG_POINT_TRANSFORMS = 0x0206;
-    public static final int TAG_JPEG_Q_TABLES = 0x0207;
-    public static final int TAG_JPEG_DC_TABLES = 0x0208;
-    public static final int TAG_JPEG_AC_TABLES = 0x0209;
-
-    public static final int TAG_YCBCR_COEFFICIENTS                = 0x0211;
-    public static final int TAG_YCBCR_SUBSAMPLING                 = 0x0212;
-    public static final int TAG_YCBCR_POSITIONING                 = 0x0213;
-    public static final int TAG_REFERENCE_BLACK_WHITE             = 0x0214;
-    public static final int TAG_STRIP_ROW_COUNTS                  = 0x022f;
-    public static final int TAG_APPLICATION_NOTES                 = 0x02bc;
-
-    public static final int TAG_RELATED_IMAGE_FILE_FORMAT         = 0x1000;
-    public static final int TAG_RELATED_IMAGE_WIDTH               = 0x1001;
-    public static final int TAG_RELATED_IMAGE_HEIGHT              = 0x1002;
-
-    public static final int TAG_RATING                            = 0x4746;
-
-    public static final int TAG_CFA_REPEAT_PATTERN_DIM            = 0x828D;
-    /** There are two definitions for CFA pattern, I don't know the difference... */
-    public static final int TAG_CFA_PATTERN_2                     = 0x828E;
-    public static final int TAG_BATTERY_LEVEL                     = 0x828F;
-    public static final int TAG_COPYRIGHT                         = 0x8298;
-    /**
-     * Exposure time (reciprocal of shutter speed). Unit is second.
-     */
-    public static final int TAG_EXPOSURE_TIME                     = 0x829A;
-    /**
-     * The actual F-number(F-stop) of lens when the image was taken.
-     */
-    public static final int TAG_FNUMBER                           = 0x829D;
-    public static final int TAG_IPTC_NAA                          = 0x83BB;
-    public static final int TAG_PHOTOSHOP_SETTINGS                = 0x8649;
-    public static final int TAG_INTER_COLOR_PROFILE               = 0x8773;
-    /**
-     * Exposure program that the camera used when image was taken. '1' means
-     * manual control, '2' program normal, '3' aperture priority, '4' shutter
-     * priority, '5' program creative (slow program), '6' program action
-     * (high-speed program), '7' portrait mode, '8' landscape mode.
-     */
-    public static final int TAG_EXPOSURE_PROGRAM                  = 0x8822;
-    public static final int TAG_SPECTRAL_SENSITIVITY              = 0x8824;
-    public static final int TAG_ISO_EQUIVALENT                    = 0x8827;
-    /**
-     * Indicates the Opto-Electric Conversion Function (OECF) specified in ISO 14524.
-     * <p>
-     * OECF is the relationship between the camera optical input and the image values.
-     * <p>
-     * The values are:
-     * <ul>
-     *   <li>Two shorts, indicating respectively number of columns, and number of rows.</li>
-     *   <li>For each column, the column name in a null-terminated ASCII string.</li>
-     *   <li>For each cell, an SRATIONAL value.</li>
-     * </ul>
-     */
-    public static final int TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION = 0x8828;
-    public static final int TAG_INTERLACE                         = 0x8829;
-    public static final int TAG_TIME_ZONE_OFFSET_TIFF_EP          = 0x882A;
-    public static final int TAG_SELF_TIMER_MODE_TIFF_EP           = 0x882B;
-    /**
-     * Applies to ISO tag.
-     *
-     * 0 = Unknown
-     * 1 = Standard Output Sensitivity
-     * 2 = Recommended Exposure Index
-     * 3 = ISO Speed
-     * 4 = Standard Output Sensitivity and Recommended Exposure Index
-     * 5 = Standard Output Sensitivity and ISO Speed
-     * 6 = Recommended Exposure Index and ISO Speed
-     * 7 = Standard Output Sensitivity, Recommended Exposure Index and ISO Speed
-     */
-    public static final int TAG_SENSITIVITY_TYPE                  = 0x8830;
-    public static final int TAG_STANDARD_OUTPUT_SENSITIVITY       = 0x8831;
-    public static final int TAG_RECOMMENDED_EXPOSURE_INDEX        = 0x8832;
-    public static final int TAG_ISO_SPEED                         = 0x8833;
-    public static final int TAG_ISO_SPEED_LATITUDE_YYY            = 0x8834;
-    public static final int TAG_ISO_SPEED_LATITUDE_ZZZ            = 0x8835;
-
-    public static final int TAG_EXIF_VERSION                      = 0x9000;
-    public static final int TAG_DATETIME_ORIGINAL                 = 0x9003;
-    public static final int TAG_DATETIME_DIGITIZED                = 0x9004;
-    public static final int TAG_OFFSET_TIME                       = 0x9010;
-    public static final int TAG_OFFSET_TIME_ORIGINAL              = 0x9011;
-    public static final int TAG_OFFSET_TIME_DIGITIZED             = 0x9012;
-
-    public static final int TAG_COMPONENTS_CONFIGURATION          = 0x9101;
-    /**
-     * Average (rough estimate) compression level in JPEG bits per pixel.
-     * */
-    public static final int TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL = 0x9102;
-
-    /**
-     * Shutter speed by APEX value. To convert this value to ordinary 'Shutter Speed';
-     * calculate this value's power of 2, then reciprocal. For example, if the
-     * ShutterSpeedValue is '4', shutter speed is 1/(24)=1/16 second.
-     */
-    public static final int TAG_SHUTTER_SPEED                     = 0x9201;
-    /**
-     * The actual aperture value of lens when the image was taken. Unit is APEX.
-     * To convert this value to ordinary F-number (F-stop), calculate this value's
-     * power of root 2 (=1.4142). For example, if the ApertureValue is '5',
-     * F-number is 1.4142^5 = F5.6.
-     */
-    public static final int TAG_APERTURE                          = 0x9202;
-    public static final int TAG_BRIGHTNESS_VALUE                  = 0x9203;
-    public static final int TAG_EXPOSURE_BIAS                     = 0x9204;
-    /**
-     * Maximum aperture value of lens. You can convert to F-number by calculating
-     * power of root 2 (same process of ApertureValue:0x9202).
-     * The actual aperture value of lens when the image was taken. To convert this
-     * value to ordinary f-number(f-stop), calculate the value's power of root 2
-     * (=1.4142). For example, if the ApertureValue is '5', f-number is 1.41425^5 = F5.6.
-     */
-    public static final int TAG_MAX_APERTURE                      = 0x9205;
-    /**
-     * Indicates the distance the autofocus camera is focused to.  Tends to be less accurate as distance increases.
-     */
-    public static final int TAG_SUBJECT_DISTANCE                  = 0x9206;
-    /**
-     * Exposure metering method. '0' means unknown, '1' average, '2' center
-     * weighted average, '3' spot, '4' multi-spot, '5' multi-segment, '6' partial,
-     * '255' other.
-     */
-    public static final int TAG_METERING_MODE                     = 0x9207;
-
-    /**
-     * @deprecated use {@link com.drew.metadata.exif.ExifDirectoryBase#TAG_WHITE_BALANCE} instead.
-     */
-    @Deprecated
-    public static final int TAG_LIGHT_SOURCE                      = 0x9208;
-    /**
-     * White balance (aka light source). '0' means unknown, '1' daylight,
-     * '2' fluorescent, '3' tungsten, '10' flash, '17' standard light A,
-     * '18' standard light B, '19' standard light C, '20' D55, '21' D65,
-     * '22' D75, '255' other.
-     */
-    public static final int TAG_WHITE_BALANCE                     = 0x9208;
-    /**
-     * 0x0  = 0000000 = No Flash
-     * 0x1  = 0000001 = Fired
-     * 0x5  = 0000101 = Fired, Return not detected
-     * 0x7  = 0000111 = Fired, Return detected
-     * 0x9  = 0001001 = On
-     * 0xd  = 0001101 = On, Return not detected
-     * 0xf  = 0001111 = On, Return detected
-     * 0x10 = 0010000 = Off
-     * 0x18 = 0011000 = Auto, Did not fire
-     * 0x19 = 0011001 = Auto, Fired
-     * 0x1d = 0011101 = Auto, Fired, Return not detected
-     * 0x1f = 0011111 = Auto, Fired, Return detected
-     * 0x20 = 0100000 = No flash function
-     * 0x41 = 1000001 = Fired, Red-eye reduction
-     * 0x45 = 1000101 = Fired, Red-eye reduction, Return not detected
-     * 0x47 = 1000111 = Fired, Red-eye reduction, Return detected
-     * 0x49 = 1001001 = On, Red-eye reduction
-     * 0x4d = 1001101 = On, Red-eye reduction, Return not detected
-     * 0x4f = 1001111 = On, Red-eye reduction, Return detected
-     * 0x59 = 1011001 = Auto, Fired, Red-eye reduction
-     * 0x5d = 1011101 = Auto, Fired, Red-eye reduction, Return not detected
-     * 0x5f = 1011111 = Auto, Fired, Red-eye reduction, Return detected
-     *        6543210 (positions)
-     *
-     * This is a bitmask.
-     * 0 = flash fired
-     * 1 = return detected
-     * 2 = return able to be detected
-     * 3 = unknown
-     * 4 = auto used
-     * 5 = unknown
-     * 6 = red eye reduction used
-     */
-    public static final int TAG_FLASH                             = 0x9209;
-    /**
-     * Focal length of lens used to take image.  Unit is millimeter.
-     * Nice digital cameras actually save the focal length as a function of how far they are zoomed in.
-     */
-    public static final int TAG_FOCAL_LENGTH                      = 0x920A;
-
-    public static final int TAG_FLASH_ENERGY_TIFF_EP              = 0x920B;
-    public static final int TAG_SPATIAL_FREQ_RESPONSE_TIFF_EP     = 0x920C;
-    public static final int TAG_NOISE                             = 0x920D;
-    public static final int TAG_FOCAL_PLANE_X_RESOLUTION_TIFF_EP  = 0x920E;
-    public static final int TAG_FOCAL_PLANE_Y_RESOLUTION_TIFF_EP = 0x920F;
-    public static final int TAG_IMAGE_NUMBER                      = 0x9211;
-    public static final int TAG_SECURITY_CLASSIFICATION           = 0x9212;
-    public static final int TAG_IMAGE_HISTORY                     = 0x9213;
-    public static final int TAG_SUBJECT_LOCATION_TIFF_EP          = 0x9214;
-    public static final int TAG_EXPOSURE_INDEX_TIFF_EP            = 0x9215;
-    public static final int TAG_STANDARD_ID_TIFF_EP               = 0x9216;
-
-    /**
-     * This tag holds the Exif Makernote. Makernotes are free to be in any format, though they are often IFDs.
-     * To determine the format, we consider the starting bytes of the makernote itself and sometimes the
-     * camera model and make.
-     * <p>
-     * The component count for this tag includes all of the bytes needed for the makernote.
-     */
-    public static final int TAG_MAKERNOTE                         = 0x927C;
-
-    public static final int TAG_USER_COMMENT                      = 0x9286;
-
-    public static final int TAG_SUBSECOND_TIME                    = 0x9290;
-    public static final int TAG_SUBSECOND_TIME_ORIGINAL           = 0x9291;
-    public static final int TAG_SUBSECOND_TIME_DIGITIZED          = 0x9292;
-
-    public static final int TAG_TEMPERATURE                       = 0x9400;
-    public static final int TAG_HUMIDITY                          = 0x9401;
-    public static final int TAG_PRESSURE                          = 0x9402;
-    public static final int TAG_WATER_DEPTH                       = 0x9403;
-    public static final int TAG_ACCELERATION                      = 0x9404;
-    public static final int TAG_CAMERA_ELEVATION_ANGLE            = 0x9405;
-
-    /** The image title, as used by Windows XP. */
-    public static final int TAG_WIN_TITLE                         = 0x9C9B;
-    /** The image comment, as used by Windows XP. */
-    public static final int TAG_WIN_COMMENT                       = 0x9C9C;
-    /** The image author, as used by Windows XP (called Artist in the Windows shell). */
-    public static final int TAG_WIN_AUTHOR                        = 0x9C9D;
-    /** The image keywords, as used by Windows XP. */
-    public static final int TAG_WIN_KEYWORDS                      = 0x9C9E;
-    /** The image subject, as used by Windows XP. */
-    public static final int TAG_WIN_SUBJECT                       = 0x9C9F;
-
-    public static final int TAG_FLASHPIX_VERSION                  = 0xA000;
-    /**
-     * Defines Color Space. DCF image must use sRGB color space so value is
-     * always '1'. If the picture uses the other color space, value is
-     * '65535':Uncalibrated.
-     */
-    public static final int TAG_COLOR_SPACE                       = 0xA001;
-    public static final int TAG_EXIF_IMAGE_WIDTH                  = 0xA002;
-    public static final int TAG_EXIF_IMAGE_HEIGHT                 = 0xA003;
-    public static final int TAG_RELATED_SOUND_FILE                = 0xA004;
-
-    public static final int TAG_FLASH_ENERGY                      = 0xA20B;
-    public static final int TAG_SPATIAL_FREQ_RESPONSE             = 0xA20C;
-    public static final int TAG_FOCAL_PLANE_X_RESOLUTION          = 0xA20E;
-    public static final int TAG_FOCAL_PLANE_Y_RESOLUTION          = 0xA20F;
-    /**
-     * Unit of FocalPlaneXResolution/FocalPlaneYResolution. '1' means no-unit,
-     * '2' inch, '3' centimeter.
-     *
-     * Note: Some of Fujifilm's digicam(e.g.FX2700,FX2900,Finepix4700Z/40i etc)
-     * uses value '3' so it must be 'centimeter', but it seems that they use a
-     * '8.3mm?'(1/3in.?) to their ResolutionUnit. Fuji's BUG? Finepix4900Z has
-     * been changed to use value '2' but it doesn't match to actual value also.
-     */
-    public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT       = 0xA210;
-    public static final int TAG_SUBJECT_LOCATION                  = 0xA214;
-    public static final int TAG_EXPOSURE_INDEX                    = 0xA215;
-    public static final int TAG_SENSING_METHOD                    = 0xA217;
-
-    public static final int TAG_FILE_SOURCE                       = 0xA300;
-    public static final int TAG_SCENE_TYPE                        = 0xA301;
-    public static final int TAG_CFA_PATTERN                       = 0xA302;
-
-    /**
-     * This tag indicates the use of special processing on image data, such as rendering
-     * geared to output. When special processing is performed, the reader is expected to
-     * disable or minimize any further processing.
-     * Tag = 41985 (A401.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = 0
-     *   0 = Normal process
-     *   1 = Custom process
-     *   Other = reserved
-     */
-    public static final int TAG_CUSTOM_RENDERED                   = 0xA401;
-    /**
-     * This tag indicates the exposure mode set when the image was shot. In auto-bracketing
-     * mode, the camera shoots a series of frames of the same scene at different exposure settings.
-     * Tag = 41986 (A402.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = none
-     *   0 = Auto exposure
-     *   1 = Manual exposure
-     *   2 = Auto bracket
-     *   Other = reserved
-     */
-    public static final int TAG_EXPOSURE_MODE                     = 0xA402;
-    /**
-     * This tag indicates the white balance mode set when the image was shot.
-     * Tag = 41987 (A403.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = none
-     *   0 = Auto white balance
-     *   1 = Manual white balance
-     *   Other = reserved
-     */
-    public static final int TAG_WHITE_BALANCE_MODE                = 0xA403;
-    /**
-     * This tag indicates the digital zoom ratio when the image was shot. If the
-     * numerator of the recorded value is 0, this indicates that digital zoom was
-     * not used.
-     * Tag = 41988 (A404.H)
-     * Type = RATIONAL
-     * Count = 1
-     * Default = none
-     */
-    public static final int TAG_DIGITAL_ZOOM_RATIO                = 0xA404;
-    /**
-     * This tag indicates the equivalent focal length assuming a 35mm film camera,
-     * in mm. A value of 0 means the focal length is unknown. Note that this tag
-     * differs from the FocalLength tag.
-     * Tag = 41989 (A405.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = none
-     */
-    public static final int TAG_35MM_FILM_EQUIV_FOCAL_LENGTH      = 0xA405;
-    /**
-     * This tag indicates the type of scene that was shot. It can also be used to
-     * record the mode in which the image was shot. Note that this differs from
-     * the scene type (SceneType) tag.
-     * Tag = 41990 (A406.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = 0
-     *   0 = Standard
-     *   1 = Landscape
-     *   2 = Portrait
-     *   3 = Night scene
-     *   Other = reserved
-     */
-    public static final int TAG_SCENE_CAPTURE_TYPE                = 0xA406;
-    /**
-     * This tag indicates the degree of overall image gain adjustment.
-     * Tag = 41991 (A407.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = none
-     *   0 = None
-     *   1 = Low gain up
-     *   2 = High gain up
-     *   3 = Low gain down
-     *   4 = High gain down
-     *   Other = reserved
-     */
-    public static final int TAG_GAIN_CONTROL                      = 0xA407;
-    /**
-     * This tag indicates the direction of contrast processing applied by the camera
-     * when the image was shot.
-     * Tag = 41992 (A408.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = 0
-     *   0 = Normal
-     *   1 = Soft
-     *   2 = Hard
-     *   Other = reserved
-     */
-    public static final int TAG_CONTRAST                          = 0xA408;
-    /**
-     * This tag indicates the direction of saturation processing applied by the camera
-     * when the image was shot.
-     * Tag = 41993 (A409.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = 0
-     *   0 = Normal
-     *   1 = Low saturation
-     *   2 = High saturation
-     *   Other = reserved
-     */
-    public static final int TAG_SATURATION                        = 0xA409;
-    /**
-     * This tag indicates the direction of sharpness processing applied by the camera
-     * when the image was shot.
-     * Tag = 41994 (A40A.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = 0
-     *   0 = Normal
-     *   1 = Soft
-     *   2 = Hard
-     *   Other = reserved
-     */
-    public static final int TAG_SHARPNESS                         = 0xA40A;
-    /**
-     * This tag indicates information on the picture-taking conditions of a particular
-     * camera model. The tag is used only to indicate the picture-taking conditions in
-     * the reader.
-     * Tag = 41995 (A40B.H)
-     * Type = UNDEFINED
-     * Count = Any
-     * Default = none
-     *
-     * The information is recorded in the format shown below. The data is recorded
-     * in Unicode using SHORT type for the number of display rows and columns and
-     * UNDEFINED type for the camera settings. The Unicode (UCS-2) string including
-     * Signature is NULL terminated. The specifics of the Unicode string are as given
-     * in ISO/IEC 10464-1.
-     *
-     *      Length  Type        Meaning
-     *      ------+-----------+------------------
-     *      2       SHORT       Display columns
-     *      2       SHORT       Display rows
-     *      Any     UNDEFINED   Camera setting-1
-     *      Any     UNDEFINED   Camera setting-2
-     *      :       :           :
-     *      Any     UNDEFINED   Camera setting-n
-     */
-    public static final int TAG_DEVICE_SETTING_DESCRIPTION        = 0xA40B;
-    /**
-     * This tag indicates the distance to the subject.
-     * Tag = 41996 (A40C.H)
-     * Type = SHORT
-     * Count = 1
-     * Default = none
-     *   0 = unknown
-     *   1 = Macro
-     *   2 = Close view
-     *   3 = Distant view
-     *   Other = reserved
-     */
-    public static final int TAG_SUBJECT_DISTANCE_RANGE            = 0xA40C;
-
-    /**
-     * This tag indicates an identifier assigned uniquely to each image. It is
-     * recorded as an ASCII string equivalent to hexadecimal notation and 128-bit
-     * fixed length.
-     * Tag = 42016 (A420.H)
-     * Type = ASCII
-     * Count = 33
-     * Default = none
-     */
-    public static final int TAG_IMAGE_UNIQUE_ID                   = 0xA420;
-    /** String. */
-    public static final int TAG_CAMERA_OWNER_NAME                 = 0xA430;
-    /** String. */
-    public static final int TAG_BODY_SERIAL_NUMBER                = 0xA431;
-    /** An array of four Rational64u numbers giving focal and aperture ranges. */
-    public static final int TAG_LENS_SPECIFICATION                = 0xA432;
-    /** String. */
-    public static final int TAG_LENS_MAKE                         = 0xA433;
-    /** String. */
-    public static final int TAG_LENS_MODEL                        = 0xA434;
-    /** String. */
-    public static final int TAG_LENS_SERIAL_NUMBER                = 0xA435;
-    /** Rational64u. */
-    public static final int TAG_GAMMA                             = 0xA500;
-
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO         = 0xC4A5;
-
-    public static final int TAG_PANASONIC_TITLE                   = 0xC6D2;
-    public static final int TAG_PANASONIC_TITLE_2                 = 0xC6D3;
-
-    public static final int TAG_PADDING                           = 0xEA1C;
-
-    public static final int TAG_LENS                              = 0xFDEA;
-
-    protected static void addExifTagNames(HashMap<Integer, String> map)
-    {
-        map.put(TAG_INTEROP_INDEX, "Interoperability Index");
-        map.put(TAG_INTEROP_VERSION, "Interoperability Version");
-        map.put(TAG_NEW_SUBFILE_TYPE, "New Subfile Type");
-        map.put(TAG_SUBFILE_TYPE, "Subfile Type");
-        map.put(TAG_IMAGE_WIDTH, "Image Width");
-        map.put(TAG_IMAGE_HEIGHT, "Image Height");
-        map.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample");
-        map.put(TAG_COMPRESSION, "Compression");
-        map.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation");
-        map.put(TAG_THRESHOLDING, "Thresholding");
-        map.put(TAG_FILL_ORDER, "Fill Order");
-        map.put(TAG_DOCUMENT_NAME, "Document Name");
-        map.put(TAG_IMAGE_DESCRIPTION, "Image Description");
-        map.put(TAG_MAKE, "Make");
-        map.put(TAG_MODEL, "Model");
-        map.put(TAG_STRIP_OFFSETS, "Strip Offsets");
-        map.put(TAG_ORIENTATION, "Orientation");
-        map.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel");
-        map.put(TAG_ROWS_PER_STRIP, "Rows Per Strip");
-        map.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts");
-        map.put(TAG_MIN_SAMPLE_VALUE, "Minimum Sample Value");
-        map.put(TAG_MAX_SAMPLE_VALUE, "Maximum Sample Value");
-        map.put(TAG_X_RESOLUTION, "X Resolution");
-        map.put(TAG_Y_RESOLUTION, "Y Resolution");
-        map.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration");
-        map.put(TAG_PAGE_NAME, "Page Name");
-        map.put(TAG_RESOLUTION_UNIT, "Resolution Unit");
-        map.put(TAG_PAGE_NUMBER, "Page Number");
-        map.put(TAG_TRANSFER_FUNCTION, "Transfer Function");
-        map.put(TAG_SOFTWARE, "Software");
-        map.put(TAG_DATETIME, "Date/Time");
-        map.put(TAG_ARTIST, "Artist");
-        map.put(TAG_PREDICTOR, "Predictor");
-        map.put(TAG_HOST_COMPUTER, "Host Computer");
-        map.put(TAG_WHITE_POINT, "White Point");
-        map.put(TAG_PRIMARY_CHROMATICITIES, "Primary Chromaticities");
-        map.put(TAG_TILE_WIDTH, "Tile Width");
-        map.put(TAG_TILE_LENGTH, "Tile Length");
-        map.put(TAG_TILE_OFFSETS, "Tile Offsets");
-        map.put(TAG_TILE_BYTE_COUNTS, "Tile Byte Counts");
-        map.put(TAG_SUB_IFD_OFFSET, "Sub IFD Pointer(s)");
-        map.put(TAG_TRANSFER_RANGE, "Transfer Range");
-        map.put(TAG_JPEG_TABLES, "JPEG Tables");
-        map.put(TAG_JPEG_PROC, "JPEG Proc");
-
-        map.put(TAG_JPEG_RESTART_INTERVAL, "JPEG Restart Interval");
-        map.put(TAG_JPEG_LOSSLESS_PREDICTORS, "JPEG Lossless Predictors");
-        map.put(TAG_JPEG_POINT_TRANSFORMS, "JPEG Point Transforms");
-        map.put(TAG_JPEG_Q_TABLES, "JPEGQ Tables");
-        map.put(TAG_JPEG_DC_TABLES, "JPEGDC Tables");
-        map.put(TAG_JPEG_AC_TABLES, "JPEGAC Tables");
-
-        map.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients");
-        map.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling");
-        map.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning");
-        map.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White");
-        map.put(TAG_STRIP_ROW_COUNTS, "Strip Row Counts");
-        map.put(TAG_APPLICATION_NOTES, "Application Notes");
-        map.put(TAG_RELATED_IMAGE_FILE_FORMAT, "Related Image File Format");
-        map.put(TAG_RELATED_IMAGE_WIDTH, "Related Image Width");
-        map.put(TAG_RELATED_IMAGE_HEIGHT, "Related Image Height");
-        map.put(TAG_RATING, "Rating");
-        map.put(TAG_CFA_REPEAT_PATTERN_DIM, "CFA Repeat Pattern Dim");
-        map.put(TAG_CFA_PATTERN_2, "CFA Pattern");
-        map.put(TAG_BATTERY_LEVEL, "Battery Level");
-        map.put(TAG_COPYRIGHT, "Copyright");
-        map.put(TAG_EXPOSURE_TIME, "Exposure Time");
-        map.put(TAG_FNUMBER, "F-Number");
-        map.put(TAG_IPTC_NAA, "IPTC/NAA");
-        map.put(TAG_PHOTOSHOP_SETTINGS, "Photoshop Settings");
-        map.put(TAG_INTER_COLOR_PROFILE, "Inter Color Profile");
-        map.put(TAG_EXPOSURE_PROGRAM, "Exposure Program");
-        map.put(TAG_SPECTRAL_SENSITIVITY, "Spectral Sensitivity");
-        map.put(TAG_ISO_EQUIVALENT, "ISO Speed Ratings");
-        map.put(TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION, "Opto-electric Conversion Function (OECF)");
-        map.put(TAG_INTERLACE, "Interlace");
-        map.put(TAG_TIME_ZONE_OFFSET_TIFF_EP, "Time Zone Offset");
-        map.put(TAG_SELF_TIMER_MODE_TIFF_EP, "Self Timer Mode");
-        map.put(TAG_SENSITIVITY_TYPE, "Sensitivity Type");
-        map.put(TAG_STANDARD_OUTPUT_SENSITIVITY, "Standard Output Sensitivity");
-        map.put(TAG_RECOMMENDED_EXPOSURE_INDEX, "Recommended Exposure Index");
-        map.put(TAG_ISO_SPEED, "ISO Speed");
-        map.put(TAG_ISO_SPEED_LATITUDE_YYY, "ISO Speed Latitude yyy");
-        map.put(TAG_ISO_SPEED_LATITUDE_ZZZ, "ISO Speed Latitude zzz");
-        map.put(TAG_EXIF_VERSION, "Exif Version");
-        map.put(TAG_DATETIME_ORIGINAL, "Date/Time Original");
-        map.put(TAG_DATETIME_DIGITIZED, "Date/Time Digitized");
-        map.put(TAG_OFFSET_TIME, "Offset Time");
-        map.put(TAG_OFFSET_TIME_ORIGINAL, "Offset Time Original");
-        map.put(TAG_OFFSET_TIME_DIGITIZED, "Offset Time Digitized");
-        map.put(TAG_COMPONENTS_CONFIGURATION, "Components Configuration");
-        map.put(TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL, "Compressed Bits Per Pixel");
-        map.put(TAG_SHUTTER_SPEED, "Shutter Speed Value");
-        map.put(TAG_APERTURE, "Aperture Value");
-        map.put(TAG_BRIGHTNESS_VALUE, "Brightness Value");
-        map.put(TAG_EXPOSURE_BIAS, "Exposure Bias Value");
-        map.put(TAG_MAX_APERTURE, "Max Aperture Value");
-        map.put(TAG_SUBJECT_DISTANCE, "Subject Distance");
-        map.put(TAG_METERING_MODE, "Metering Mode");
-        map.put(TAG_WHITE_BALANCE, "White Balance");
-        map.put(TAG_FLASH, "Flash");
-        map.put(TAG_FOCAL_LENGTH, "Focal Length");
-        map.put(TAG_FLASH_ENERGY_TIFF_EP, "Flash Energy");
-        map.put(TAG_SPATIAL_FREQ_RESPONSE_TIFF_EP, "Spatial Frequency Response");
-        map.put(TAG_NOISE, "Noise");
-        map.put(TAG_FOCAL_PLANE_X_RESOLUTION_TIFF_EP, "Focal Plane X Resolution");
-        map.put(TAG_FOCAL_PLANE_Y_RESOLUTION_TIFF_EP, "Focal Plane Y Resolution");
-        map.put(TAG_IMAGE_NUMBER, "Image Number");
-        map.put(TAG_SECURITY_CLASSIFICATION, "Security Classification");
-        map.put(TAG_IMAGE_HISTORY, "Image History");
-        map.put(TAG_SUBJECT_LOCATION_TIFF_EP, "Subject Location");
-        map.put(TAG_EXPOSURE_INDEX_TIFF_EP, "Exposure Index");
-        map.put(TAG_STANDARD_ID_TIFF_EP, "TIFF/EP Standard ID");
-        map.put(TAG_MAKERNOTE, "Makernote");
-        map.put(TAG_USER_COMMENT, "User Comment");
-        map.put(TAG_SUBSECOND_TIME, "Sub-Sec Time");
-        map.put(TAG_SUBSECOND_TIME_ORIGINAL, "Sub-Sec Time Original");
-        map.put(TAG_SUBSECOND_TIME_DIGITIZED, "Sub-Sec Time Digitized");
-        map.put(TAG_TEMPERATURE, "Temperature");
-        map.put(TAG_HUMIDITY, "Humidity");
-        map.put(TAG_PRESSURE, "Pressure");
-        map.put(TAG_WATER_DEPTH, "Water Depth");
-        map.put(TAG_ACCELERATION, "Acceleration");
-        map.put(TAG_CAMERA_ELEVATION_ANGLE, "Camera Elevation Angle");
-        map.put(TAG_WIN_TITLE, "Windows XP Title");
-        map.put(TAG_WIN_COMMENT, "Windows XP Comment");
-        map.put(TAG_WIN_AUTHOR, "Windows XP Author");
-        map.put(TAG_WIN_KEYWORDS, "Windows XP Keywords");
-        map.put(TAG_WIN_SUBJECT, "Windows XP Subject");
-        map.put(TAG_FLASHPIX_VERSION, "FlashPix Version");
-        map.put(TAG_COLOR_SPACE, "Color Space");
-        map.put(TAG_EXIF_IMAGE_WIDTH, "Exif Image Width");
-        map.put(TAG_EXIF_IMAGE_HEIGHT, "Exif Image Height");
-        map.put(TAG_RELATED_SOUND_FILE, "Related Sound File");
-        map.put(TAG_FLASH_ENERGY, "Flash Energy");
-        map.put(TAG_SPATIAL_FREQ_RESPONSE, "Spatial Frequency Response");
-        map.put(TAG_FOCAL_PLANE_X_RESOLUTION, "Focal Plane X Resolution");
-        map.put(TAG_FOCAL_PLANE_Y_RESOLUTION, "Focal Plane Y Resolution");
-        map.put(TAG_FOCAL_PLANE_RESOLUTION_UNIT, "Focal Plane Resolution Unit");
-        map.put(TAG_SUBJECT_LOCATION, "Subject Location");
-        map.put(TAG_EXPOSURE_INDEX, "Exposure Index");
-        map.put(TAG_SENSING_METHOD, "Sensing Method");
-        map.put(TAG_FILE_SOURCE, "File Source");
-        map.put(TAG_SCENE_TYPE, "Scene Type");
-        map.put(TAG_CFA_PATTERN, "CFA Pattern");
-        map.put(TAG_CUSTOM_RENDERED, "Custom Rendered");
-        map.put(TAG_EXPOSURE_MODE, "Exposure Mode");
-        map.put(TAG_WHITE_BALANCE_MODE, "White Balance Mode");
-        map.put(TAG_DIGITAL_ZOOM_RATIO, "Digital Zoom Ratio");
-        map.put(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH, "Focal Length 35");
-        map.put(TAG_SCENE_CAPTURE_TYPE, "Scene Capture Type");
-        map.put(TAG_GAIN_CONTROL, "Gain Control");
-        map.put(TAG_CONTRAST, "Contrast");
-        map.put(TAG_SATURATION, "Saturation");
-        map.put(TAG_SHARPNESS, "Sharpness");
-        map.put(TAG_DEVICE_SETTING_DESCRIPTION, "Device Setting Description");
-        map.put(TAG_SUBJECT_DISTANCE_RANGE, "Subject Distance Range");
-        map.put(TAG_IMAGE_UNIQUE_ID, "Unique Image ID");
-        map.put(TAG_CAMERA_OWNER_NAME, "Camera Owner Name");
-        map.put(TAG_BODY_SERIAL_NUMBER, "Body Serial Number");
-        map.put(TAG_LENS_SPECIFICATION, "Lens Specification");
-        map.put(TAG_LENS_MAKE, "Lens Make");
-        map.put(TAG_LENS_MODEL, "Lens Model");
-        map.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
-        map.put(TAG_GAMMA, "Gamma");
-        map.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-        map.put(TAG_PANASONIC_TITLE, "Panasonic Title");
-        map.put(TAG_PANASONIC_TITLE_2, "Panasonic Title (2)");
-        map.put(TAG_PADDING, "Padding");
-        map.put(TAG_LENS, "Lens");
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifIFD0Descriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifIFD0Descriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,38 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ExifIFD0Directory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifIFD0Descriptor extends ExifDescriptorBase<ExifIFD0Directory>
-{
-    public ExifIFD0Descriptor(@NotNull ExifIFD0Directory directory)
-    {
-        super(directory);
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifIFD0Directory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifIFD0Directory.java	(revision 16024)
+++ 	(revision )
@@ -1,68 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.util.HashMap;
-
-/**
- * Describes Exif tags from the IFD0 directory.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifIFD0Directory extends ExifDirectoryBase
-{
-    /** This tag is a pointer to the Exif SubIFD. */
-    public static final int TAG_EXIF_SUB_IFD_OFFSET = 0x8769;
-
-    /** This tag is a pointer to the Exif GPS IFD. */
-    public static final int TAG_GPS_INFO_OFFSET = 0x8825;
-
-    public ExifIFD0Directory()
-    {
-        this.setDescriptor(new ExifIFD0Descriptor(this));
-    }
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        addExifTagNames(_tagNameMap);
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Exif IFD0";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifImageDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifImageDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,37 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ExifImageDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifImageDescriptor extends ExifDescriptorBase<ExifImageDirectory>
-{
-    public ExifImageDescriptor(@NotNull ExifImageDirectory directory)
-    {
-        super(directory);
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifImageDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifImageDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,64 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.util.HashMap;
-
-/**
- * Describes One of several Exif directories.
- *
- * Holds information about image IFD's in a chain after the first. The first page is stored in IFD0.
- * Currently, this only applied to multi-page TIFF images
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifImageDirectory extends ExifDirectoryBase
-{
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        addExifTagNames(_tagNameMap);
-    }
-
-    public ExifImageDirectory()
-    {
-        this.setDescriptor(new ExifImageDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Exif Image";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifInteropDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifInteropDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,37 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ExifInteropDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifInteropDescriptor extends ExifDescriptorBase<ExifInteropDirectory>
-{
-    public ExifInteropDescriptor(@NotNull ExifInteropDirectory directory)
-    {
-        super(directory);
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifInteropDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifInteropDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,61 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.util.HashMap;
-
-/**
- * Describes Exif interoperability tags.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifInteropDirectory extends ExifDirectoryBase
-{
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        addExifTagNames(_tagNameMap);
-    }
-
-    public ExifInteropDirectory()
-    {
-        this.setDescriptor(new ExifInteropDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Interoperability";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifReader.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifReader.java	(revision 16024)
+++ 	(revision )
@@ -1,102 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.imaging.tiff.TiffProcessingException;
-import com.drew.imaging.tiff.TiffReader;
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Metadata;
-
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * Decodes Exif binary data, populating a {@link Metadata} object with tag values in {@link ExifSubIFDDirectory},
- * {@link ExifThumbnailDirectory}, {@link ExifInteropDirectory}, {@link GpsDirectory} and one of the many camera
- * makernote directories.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifReader implements JpegSegmentMetadataReader
-{
-    /** Exif data stored in JPEG files' APP1 segment are preceded by this six character preamble. */
-    public static final String JPEG_SEGMENT_PREAMBLE = "Exif\0\0";
-
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.APP1);
-    }
-
-    public void readJpegSegments(@NotNull final Iterable<byte[]> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType)
-    {
-        assert(segmentType == JpegSegmentType.APP1);
-
-        for (byte[] segmentBytes : segments) {
-            // Filter any segments containing unexpected preambles
-            if (segmentBytes.length < JPEG_SEGMENT_PREAMBLE.length() || !new String(segmentBytes, 0, JPEG_SEGMENT_PREAMBLE.length()).equals(JPEG_SEGMENT_PREAMBLE))
-                continue;
-            extract(new ByteArrayReader(segmentBytes), metadata, JPEG_SEGMENT_PREAMBLE.length());
-        }
-    }
-
-    /** Reads TIFF formatted Exif data from start of the specified {@link RandomAccessReader}. */
-    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata)
-    {
-        extract(reader, metadata, 0);
-    }
-
-    /** Reads TIFF formatted Exif data a specified offset within a {@link RandomAccessReader}. */
-    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset)
-    {
-        extract(reader, metadata, readerOffset, null);
-    }
-
-    /** Reads TIFF formatted Exif data at a specified offset within a {@link RandomAccessReader}. */
-    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, int readerOffset, @Nullable Directory parentDirectory)
-    {
-        ExifTiffHandler exifTiffHandler = new ExifTiffHandler(metadata, parentDirectory);
-
-        try {
-            // Read the TIFF-formatted Exif data
-            new TiffReader().processTiff(
-                reader,
-                exifTiffHandler,
-                readerOffset
-            );
-        } catch (TiffProcessingException e) {
-            exifTiffHandler.error("Exception processing TIFF data: " + e.getMessage());
-            // TODO what do to with this error state?
-            e.printStackTrace(System.err);
-        } catch (IOException e) {
-            exifTiffHandler.error("Exception processing TIFF data: " + e.getMessage());
-            // TODO what do to with this error state?
-            e.printStackTrace(System.err);
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifSubIFDDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifSubIFDDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,37 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ExifSubIFDDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifSubIFDDescriptor extends ExifDescriptorBase<ExifSubIFDDirectory>
-{
-    public ExifSubIFDDescriptor(@NotNull ExifSubIFDDirectory directory)
-    {
-        super(directory);
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifSubIFDDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifSubIFDDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,177 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.TimeZone;
-
-/**
- * Describes Exif tags from the SubIFD directory.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifSubIFDDirectory extends ExifDirectoryBase
-{
-    /** This tag is a pointer to the Exif Interop IFD. */
-    public static final int TAG_INTEROP_OFFSET = 0xA005;
-
-    public ExifSubIFDDirectory()
-    {
-        this.setDescriptor(new ExifSubIFDDescriptor(this));
-    }
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        addExifTagNames(_tagNameMap);
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Exif SubIFD";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    /**
-     * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date
-     * object with milliseconds representing the date and time when this image was modified.  If
-     * the time offset tag does not exist, attempts will be made to parse the values as though it is
-     * in the GMT {@link TimeZone}.
-     *
-     * @return A Date object representing when this image was modified, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateModified()
-    {
-        return getDateModified(null);
-    }
-
-    /**
-     * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date
-     * object with milliseconds representing the date and time when this image was modified.  If
-     * the time offset tag does not exist, attempts will be made to parse the values as though it is
-     * in the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).
-     *
-     * @param timeZone the time zone to use
-     * @return A Date object representing when this image was modified, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateModified(@Nullable TimeZone timeZone)
-    {
-        Directory parent = getParent();
-        if (parent instanceof ExifIFD0Directory) {
-            TimeZone timeZoneModified = getTimeZone(TAG_OFFSET_TIME);
-            return parent.getDate(TAG_DATETIME, getString(TAG_SUBSECOND_TIME),
-                (timeZoneModified != null) ? timeZoneModified : timeZone);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date
-     * object with milliseconds representing the date and time when this image was captured.  If
-     * the time offset tag does not exist, attempts will be made to parse the values as though it is
-     * in the GMT {@link TimeZone}.
-     *
-     * @return A Date object representing when this image was captured, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateOriginal()
-    {
-        return getDateOriginal(null);
-    }
-
-    /**
-     * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date
-     * object with milliseconds representing the date and time when this image was captured.  If
-     * the time offset tag does not exist, attempts will be made to parse the values as though it is
-     * in the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).
-     *
-     * @param timeZone the time zone to use
-     * @return A Date object representing when this image was captured, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateOriginal(@Nullable TimeZone timeZone)
-    {
-        TimeZone timeZoneOriginal = getTimeZone(TAG_OFFSET_TIME_ORIGINAL);
-        return getDate(TAG_DATETIME_ORIGINAL, getString(TAG_SUBSECOND_TIME_ORIGINAL),
-            (timeZoneOriginal != null) ? timeZoneOriginal : timeZone);
-    }
-
-    /**
-     * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date
-     * object with milliseconds representing the date and time when this image was digitized.  If
-     * the time offset tag does not exist, attempts will be made to parse the values as though it is
-     * in the GMT {@link TimeZone}.
-     *
-     * @return A Date object representing when this image was digitized, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateDigitized()
-    {
-        return getDateDigitized(null);
-    }
-
-    /**
-     * Parses the date/time tag, the subsecond tag and the time offset tag to obtain a single Date
-     * object with milliseconds representing the date and time when this image was digitized.  If
-     * the time offset tag does not exist, attempts will be made to parse the values as though it is
-     * in the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null).
-     *
-     * @param timeZone the time zone to use
-     * @return A Date object representing when this image was digitized, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateDigitized(@Nullable TimeZone timeZone)
-    {
-        TimeZone timeZoneDigitized = getTimeZone(TAG_OFFSET_TIME_DIGITIZED);
-        return getDate(TAG_DATETIME_DIGITIZED, getString(TAG_SUBSECOND_TIME_DIGITIZED),
-            (timeZoneDigitized != null) ? timeZoneDigitized : timeZone);
-    }
-
-    @Nullable
-    private TimeZone getTimeZone(int tagType)
-    {
-        String timeOffset = getString(tagType);
-        if (timeOffset != null && timeOffset.matches("[\\+\\-]\\d\\d:\\d\\d")) {
-            return TimeZone.getTimeZone("GMT" + timeOffset);
-        } else {
-            return null;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifThumbnailDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifThumbnailDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,69 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import static com.drew.metadata.exif.ExifThumbnailDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ExifThumbnailDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifThumbnailDescriptor extends ExifDescriptorBase<ExifThumbnailDirectory>
-{
-    public ExifThumbnailDescriptor(@NotNull ExifThumbnailDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_THUMBNAIL_OFFSET:
-                return getThumbnailOffsetDescription();
-            case TAG_THUMBNAIL_LENGTH:
-                return getThumbnailLengthDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getThumbnailLengthDescription()
-    {
-        String value = _directory.getString(TAG_THUMBNAIL_LENGTH);
-        return value == null ? null : value + " bytes";
-    }
-
-    @Nullable
-    public String getThumbnailOffsetDescription()
-    {
-        String value = _directory.getString(TAG_THUMBNAIL_OFFSET);
-        return value == null ? null : value + " bytes";
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,80 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.util.HashMap;
-
-/**
- * One of several Exif directories.  Otherwise known as IFD1, this directory holds information about an embedded thumbnail image.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ExifThumbnailDirectory extends ExifDirectoryBase
-{
-    /**
-     * The offset to thumbnail image bytes.
-     */
-    public static final int TAG_THUMBNAIL_OFFSET = 0x0201;
-    /**
-     * The size of the thumbnail image data in bytes.
-     */
-    public static final int TAG_THUMBNAIL_LENGTH = 0x0202;
-
-    /**
-     * @deprecated use {@link com.drew.metadata.exif.ExifDirectoryBase#TAG_COMPRESSION} instead.
-     */
-    @Deprecated
-    public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        addExifTagNames(_tagNameMap);
-
-        _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset");
-        _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length");
-    }
-
-    public ExifThumbnailDirectory()
-    {
-        this.setDescriptor(new ExifThumbnailDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Exif Thumbnail";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/ExifTiffHandler.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/ExifTiffHandler.java	(revision 16024)
+++ 	(revision )
@@ -1,859 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Set;
-
-import com.drew.imaging.jpeg.JpegMetadataReader;
-import com.drew.imaging.jpeg.JpegProcessingException;
-import com.drew.imaging.tiff.TiffProcessingException;
-import com.drew.imaging.tiff.TiffReader;
-import com.drew.lang.BufferBoundsException;
-import com.drew.lang.Charsets;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.StringValue;
-import com.drew.metadata.exif.makernotes.AppleMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.CanonMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.CasioType1MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.CasioType2MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.KodakMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.KyoceraMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.LeicaMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.LeicaType5MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.NikonType1MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.NikonType2MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusEquipmentMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusFocusInfoMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusRawDevelopment2MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusRawDevelopmentMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.OlympusRawInfoMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.PanasonicMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.PentaxMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.ReconyxHyperFireMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.ReconyxUltraFireMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.RicohMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.SamsungType2MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.SanyoMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.SigmaMakernoteDirectory;
-import com.drew.metadata.exif.makernotes.SonyType1MakernoteDirectory;
-import com.drew.metadata.exif.makernotes.SonyType6MakernoteDirectory;
-import com.drew.metadata.iptc.IptcReader;
-import com.drew.metadata.tiff.DirectoryTiffHandler;
-
-/**
- * Implementation of {@link com.drew.imaging.tiff.TiffHandler} used for handling TIFF tags according to the Exif
- * standard.
- * <p>
- * Includes support for camera manufacturer makernotes.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class ExifTiffHandler extends DirectoryTiffHandler
-{
-    public ExifTiffHandler(@NotNull Metadata metadata, @Nullable Directory parentDirectory)
-    {
-        super(metadata, parentDirectory);
-    }
-
-    public void setTiffMarker(int marker) throws TiffProcessingException
-    {
-        final int standardTiffMarker = 0x002A;
-        final int olympusRawTiffMarker = 0x4F52; // for ORF files
-        final int olympusRawTiffMarker2 = 0x5352; // for ORF files
-        final int panasonicRawTiffMarker = 0x0055; // for RW2 files
-
-        switch (marker) {
-            case standardTiffMarker:
-            case olympusRawTiffMarker:      // TODO implement an IFD0, if there is one
-            case olympusRawTiffMarker2:     // TODO implement an IFD0, if there is one
-                pushDirectory(ExifIFD0Directory.class);
-                break;
-            case panasonicRawTiffMarker:
-                pushDirectory(PanasonicRawIFD0Directory.class);
-                break;
-            default:
-                throw new TiffProcessingException(String.format("Unexpected TIFF marker: 0x%X", marker));
-        }
-    }
-
-    public boolean tryEnterSubIfd(int tagId)
-    {
-        if (tagId == ExifDirectoryBase.TAG_SUB_IFD_OFFSET) {
-            pushDirectory(ExifSubIFDDirectory.class);
-            return true;
-        }
-
-        if (_currentDirectory instanceof ExifIFD0Directory || _currentDirectory instanceof PanasonicRawIFD0Directory) {
-            if (tagId == ExifIFD0Directory.TAG_EXIF_SUB_IFD_OFFSET) {
-                pushDirectory(ExifSubIFDDirectory.class);
-                return true;
-            }
-
-            if (tagId == ExifIFD0Directory.TAG_GPS_INFO_OFFSET) {
-                pushDirectory(GpsDirectory.class);
-                return true;
-            }
-        }
-
-        if (_currentDirectory instanceof ExifSubIFDDirectory) {
-            if (tagId == ExifSubIFDDirectory.TAG_INTEROP_OFFSET) {
-                pushDirectory(ExifInteropDirectory.class);
-                return true;
-            }
-        }
-
-        if (_currentDirectory instanceof OlympusMakernoteDirectory) {
-            // Note: these also appear in customProcessTag because some are IFD pointers while others begin immediately
-            // for the same directories
-            switch(tagId) {
-                case OlympusMakernoteDirectory.TAG_EQUIPMENT:
-                    pushDirectory(OlympusEquipmentMakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_CAMERA_SETTINGS:
-                    pushDirectory(OlympusCameraSettingsMakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_RAW_DEVELOPMENT:
-                    pushDirectory(OlympusRawDevelopmentMakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_RAW_DEVELOPMENT_2:
-                    pushDirectory(OlympusRawDevelopment2MakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_IMAGE_PROCESSING:
-                    pushDirectory(OlympusImageProcessingMakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_FOCUS_INFO:
-                    pushDirectory(OlympusFocusInfoMakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_RAW_INFO:
-                    pushDirectory(OlympusRawInfoMakernoteDirectory.class);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_MAIN_INFO:
-                    pushDirectory(OlympusMakernoteDirectory.class);
-                    return true;
-            }
-        }
-
-        return false;
-    }
-
-    public boolean hasFollowerIfd()
-    {
-        // In Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case.
-        // UPDATE: In multipage TIFFs, the 'follower' IFD points to the next image in the set
-        if (_currentDirectory instanceof ExifIFD0Directory || _currentDirectory instanceof ExifImageDirectory) {
-            // If the PageNumber tag is defined, assume this is a multipage TIFF or similar
-            // TODO: Find better ways to know which follower Directory should be used
-            if (_currentDirectory.containsTag(ExifDirectoryBase.TAG_PAGE_NUMBER))
-                pushDirectory(ExifImageDirectory.class);
-            else
-                pushDirectory(ExifThumbnailDirectory.class);
-            return true;
-        }
-
-        // The Canon EOS 7D (CR2) has three chained/following thumbnail IFDs
-        if (_currentDirectory instanceof ExifThumbnailDirectory)
-            return true;
-
-        // This should not happen, as Exif doesn't use follower IFDs apart from that above.
-        // NOTE have seen the CanonMakernoteDirectory IFD have a follower pointer, but it points to invalid data.
-        return false;
-    }
-
-    @Nullable
-    public Long tryCustomProcessFormat(final int tagId, final int formatCode, final long componentCount)
-    {
-        if (formatCode == 13)
-            return componentCount * 4;
-
-        // an unknown (0) formatCode needs to be potentially handled later as a highly custom directory tag
-        if (formatCode == 0)
-            return 0L;
-
-        return null;
-    }
-
-    public boolean customProcessTag(final int tagOffset,
-                                    final @NotNull Set<Integer> processedIfdOffsets,
-                                    final int tiffHeaderOffset,
-                                    final @NotNull RandomAccessReader reader,
-                                    final int tagId,
-                                    final int byteCount) throws IOException
-    {
-        assert(_currentDirectory != null);
-
-        // Some 0x0000 tags have a 0 byteCount. Determine whether it's bad.
-        if (tagId == 0) {
-            if (_currentDirectory.containsTag(tagId)) {
-                // Let it go through for now. Some directories handle it, some don't
-                return false;
-            }
-
-            // Skip over 0x0000 tags that don't have any associated bytes. No idea what it contains in this case, if anything.
-            if (byteCount == 0)
-                return true;
-        }
-
-        // Custom processing for the Makernote tag
-        if (tagId == ExifSubIFDDirectory.TAG_MAKERNOTE && _currentDirectory instanceof ExifSubIFDDirectory) {
-            return processMakernote(tagOffset, processedIfdOffsets, tiffHeaderOffset, reader);
-        }
-
-        // Custom processing for embedded IPTC data
-        if (tagId == ExifSubIFDDirectory.TAG_IPTC_NAA && _currentDirectory instanceof ExifIFD0Directory) {
-            // NOTE Adobe sets type 4 for IPTC instead of 7
-            if (reader.getInt8(tagOffset) == 0x1c) {
-                final byte[] iptcBytes = reader.getBytes(tagOffset, byteCount);
-                new IptcReader().extract(new SequentialByteArrayReader(iptcBytes), _metadata, iptcBytes.length, _currentDirectory);
-                return true;
-            }
-            return false;
-        }
-
-        if (handlePrintIM(_currentDirectory, tagId))
-        {
-            PrintIMDirectory printIMDirectory = new PrintIMDirectory();
-            printIMDirectory.setParent(_currentDirectory);
-            _metadata.addDirectory(printIMDirectory);
-            processPrintIM(printIMDirectory, tagOffset, reader, byteCount);
-            return true;
-        }
-
-        // Note: these also appear in tryEnterSubIfd because some are IFD pointers while others begin immediately
-        // for the same directories
-        if (_currentDirectory instanceof OlympusMakernoteDirectory) {
-            switch (tagId) {
-                case OlympusMakernoteDirectory.TAG_EQUIPMENT:
-                    pushDirectory(OlympusEquipmentMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_CAMERA_SETTINGS:
-                    pushDirectory(OlympusCameraSettingsMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_RAW_DEVELOPMENT:
-                    pushDirectory(OlympusRawDevelopmentMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_RAW_DEVELOPMENT_2:
-                    pushDirectory(OlympusRawDevelopment2MakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_IMAGE_PROCESSING:
-                    pushDirectory(OlympusImageProcessingMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_FOCUS_INFO:
-                    pushDirectory(OlympusFocusInfoMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_RAW_INFO:
-                    pushDirectory(OlympusRawInfoMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-                case OlympusMakernoteDirectory.TAG_MAIN_INFO:
-                    pushDirectory(OlympusMakernoteDirectory.class);
-                    TiffReader.processIfd(this, reader, processedIfdOffsets, tagOffset, tiffHeaderOffset);
-                    return true;
-            }
-        }
-
-        if (_currentDirectory instanceof PanasonicRawIFD0Directory) {
-            // these contain binary data with specific offsets, and can't be processed as regular ifd's.
-            // The binary data is broken into 'fake' tags and there is a pattern.
-            switch (tagId) {
-                case PanasonicRawIFD0Directory.TagWbInfo:
-                    PanasonicRawWbInfoDirectory dirWbInfo = new PanasonicRawWbInfoDirectory();
-                    dirWbInfo.setParent(_currentDirectory);
-                    _metadata.addDirectory(dirWbInfo);
-                    processBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2);
-                    return true;
-                case PanasonicRawIFD0Directory.TagWbInfo2:
-                    PanasonicRawWbInfo2Directory dirWbInfo2 = new PanasonicRawWbInfo2Directory();
-                    dirWbInfo2.setParent(_currentDirectory);
-                    _metadata.addDirectory(dirWbInfo2);
-                    processBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3);
-                    return true;
-                case PanasonicRawIFD0Directory.TagDistortionInfo:
-                    PanasonicRawDistortionDirectory dirDistort = new PanasonicRawDistortionDirectory();
-                    dirDistort.setParent(_currentDirectory);
-                    _metadata.addDirectory(dirDistort);
-                    processBinary(dirDistort, tagOffset, reader, byteCount, true, 1);
-                    return true;
-            }
-        }
-
-        // Panasonic RAW sometimes contains an embedded version of the data as a JPG file.
-        if (tagId == PanasonicRawIFD0Directory.TagJpgFromRaw && _currentDirectory instanceof PanasonicRawIFD0Directory) {
-            byte[] jpegrawbytes = reader.getBytes(tagOffset, byteCount);
-
-            // Extract information from embedded image since it is metadata-rich
-            ByteArrayInputStream jpegmem = new ByteArrayInputStream(jpegrawbytes);
-            try {
-                Metadata jpegDirectory = JpegMetadataReader.readMetadata(jpegmem);
-                for (Directory directory : jpegDirectory.getDirectories()) {
-                    directory.setParent(_currentDirectory);
-                    _metadata.addDirectory(directory);
-                }
-                return true;
-            } catch (JpegProcessingException e) {
-                _currentDirectory.addError("Error processing JpgFromRaw: " + e.getMessage());
-            } catch (IOException e) {
-                _currentDirectory.addError("Error reading JpgFromRaw: " + e.getMessage());
-            }
-        }
-
-        return false;
-    }
-
-    private static void processBinary(@NotNull final Directory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount, final Boolean isSigned, final int arrayLength) throws IOException
-    {
-        // expects signed/unsigned int16 (for now)
-        //int byteSize = isSigned ? sizeof(short) : sizeof(ushort);
-        int byteSize = 2;
-
-        // 'directory' is assumed to contain tags that correspond to the byte position unless it's a set of bytes
-        for (int i = 0; i < byteCount; i++) {
-            if (directory.hasTagName(i)) {
-                // only process this tag if the 'next' integral tag exists. Otherwise, it's a set of bytes
-                if (i < byteCount - 1 && directory.hasTagName(i + 1)) {
-                    if (isSigned)
-                        directory.setObject(i, reader.getInt16(tagValueOffset + (i* byteSize)));
-                    else
-                        directory.setObject(i, reader.getUInt16(tagValueOffset + (i* byteSize)));
-                } else {
-                    // the next arrayLength bytes are a multi-byte value
-                    if (isSigned) {
-                        short[] val = new short[arrayLength];
-                        for (int j = 0; j<val.length; j++)
-                            val[j] = reader.getInt16(tagValueOffset + ((i + j) * byteSize));
-                        directory.setObjectArray(i, val);
-                    } else {
-                        int[] val = new int[arrayLength];
-                        for (int j = 0; j<val.length; j++)
-                            val[j] = reader.getUInt16(tagValueOffset + ((i + j) * byteSize));
-                        directory.setObjectArray(i, val);
-                    }
-
-                    i += arrayLength - 1;
-                }
-            }
-        }
-    }
-
-    /** Read a given number of bytes from the stream
-     *
-     * This method is employed to "suppress" attempts to read beyond end of the
-     * file as may happen at the beginning of processMakernote when we read
-     * increasingly longer camera makes.
-     *
-     * Instead of failing altogether in this context we return an empty string
-     * which will fail all sensible attempts to compare to makes while avoiding
-     * a full-on failure.
-     */
-    @NotNull
-    private static String getReaderString(final @NotNull RandomAccessReader reader, final int makernoteOffset, final int bytesRequested) throws IOException
-    {
-        try {
-            return reader.getString(makernoteOffset, bytesRequested, Charsets.UTF_8);
-        } catch(BufferBoundsException e) {
-            return "";
-        }
-    }
-
-    private boolean processMakernote(final int makernoteOffset,
-                                     final @NotNull Set<Integer> processedIfdOffsets,
-                                     final int tiffHeaderOffset,
-                                     final @NotNull RandomAccessReader reader) throws IOException
-    {
-        assert(_currentDirectory != null);
-
-        // Determine the camera model and makernote format.
-        Directory ifd0Directory = _metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
-
-        String cameraMake = ifd0Directory == null ? null : ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE);
-
-        final String firstTwoChars    = getReaderString(reader, makernoteOffset, 2);
-        final String firstThreeChars  = getReaderString(reader, makernoteOffset, 3);
-        final String firstFourChars   = getReaderString(reader, makernoteOffset, 4);
-        final String firstFiveChars   = getReaderString(reader, makernoteOffset, 5);
-        final String firstSixChars    = getReaderString(reader, makernoteOffset, 6);
-        final String firstSevenChars  = getReaderString(reader, makernoteOffset, 7);
-        final String firstEightChars  = getReaderString(reader, makernoteOffset, 8);
-        final String firstNineChars   = getReaderString(reader, makernoteOffset, 9);
-        final String firstTenChars    = getReaderString(reader, makernoteOffset, 10);
-        final String firstTwelveChars = getReaderString(reader, makernoteOffset, 12);
-
-        boolean byteOrderBefore = reader.isMotorolaByteOrder();
-
-        if ("OLYMP\0".equals(firstSixChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) {
-            // Olympus Makernote
-            // Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/
-            pushDirectory(OlympusMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset);
-        } else if ("OLYMPUS\0II".equals(firstTenChars)) {
-            // Olympus Makernote (alternate)
-            // Note that data is relative to the beginning of the makernote
-            // http://exiv2.org/makernote.html
-            pushDirectory(OlympusMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 12, makernoteOffset);
-        } else if (cameraMake != null && cameraMake.toUpperCase().startsWith("MINOLTA")) {
-            // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
-            // area that commences immediately.
-            pushDirectory(OlympusMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset);
-        } else if (cameraMake != null && cameraMake.trim().toUpperCase().startsWith("NIKON")) {
-            if ("Nikon".equals(firstFiveChars)) {
-                /* There are two scenarios here:
-                 * Type 1:                  **
-                 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
-                 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
-                 * Type 3:                  **
-                 * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
-                 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
-                 */
-                switch (reader.getUInt8(makernoteOffset + 6)) {
-                    case 1:
-                        pushDirectory(NikonType1MakernoteDirectory.class);
-                        TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset);
-                        break;
-                    case 2:
-                        pushDirectory(NikonType2MakernoteDirectory.class);
-                        TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 18, makernoteOffset + 10);
-                        break;
-                    default:
-                        _currentDirectory.addError("Unsupported Nikon makernote data ignored.");
-                        break;
-                }
-            } else {
-                // The IFD begins with the first Makernote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
-                pushDirectory(NikonType2MakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset);
-            }
-        } else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars)) {
-            pushDirectory(SonyType1MakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset);
-        // Do this check LAST after most other Sony checks
-        } else if (cameraMake != null && cameraMake.startsWith("SONY") &&
-                !Arrays.equals(reader.getBytes(makernoteOffset, 2), new byte[]{ 0x01, 0x00 }) ) {
-            // The IFD begins with the first Makernote byte (no ASCII name). Used in SR2 and ARW images
-            pushDirectory(SonyType1MakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset);
-        } else if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".equals(firstTwelveChars)) {
-            // force MM for this directory
-            reader.setMotorolaByteOrder(true);
-            // skip 12 byte header + 2 for "MM" + 6
-            pushDirectory(SonyType6MakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 20, tiffHeaderOffset);
-        } else if ("SIGMA\u0000\u0000\u0000".equals(firstEightChars) || "FOVEON\u0000\u0000".equals(firstEightChars)) {
-            pushDirectory(SigmaMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 10, tiffHeaderOffset);
-        } else if ("KDK".equals(firstThreeChars)) {
-            reader.setMotorolaByteOrder(firstSevenChars.equals("KDK INFO"));
-            KodakMakernoteDirectory directory = new KodakMakernoteDirectory();
-            _metadata.addDirectory(directory);
-            processKodakMakernote(directory, makernoteOffset, reader);
-        } else if ("Canon".equalsIgnoreCase(cameraMake)) {
-            pushDirectory(CanonMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset);
-        } else if (cameraMake != null && cameraMake.toUpperCase().startsWith("CASIO")) {
-            if ("QVC\u0000\u0000\u0000".equals(firstSixChars)) {
-                pushDirectory(CasioType2MakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 6, tiffHeaderOffset);
-            } else {
-                pushDirectory(CasioType1MakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset);
-            }
-        } else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraMake)) {
-            // Note that this also applies to certain Leica cameras, such as the Digilux-4.3
-            reader.setMotorolaByteOrder(false);
-            // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
-            // IFD, though the offset is relative to the start of the makernote, not the TIFF
-            // header (like everywhere else)
-            int ifdStart = makernoteOffset + reader.getInt32(makernoteOffset + 8);
-            pushDirectory(FujifilmMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, ifdStart, makernoteOffset);
-        } else if ("KYOCERA".equals(firstSevenChars)) {
-            // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
-            pushDirectory(KyoceraMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 22, tiffHeaderOffset);
-        } else if ("LEICA".equals(firstFiveChars)) {
-            reader.setMotorolaByteOrder(false);
-
-            // used by the X1/X2/X VARIO/T
-            // (X1 starts with "LEICA\0\x01\0", Make is "LEICA CAMERA AG")
-            // (X2 starts with "LEICA\0\x05\0", Make is "LEICA CAMERA AG")
-            // (X VARIO starts with "LEICA\0\x04\0", Make is "LEICA CAMERA AG")
-            // (T (Typ 701) starts with "LEICA\0\0x6", Make is "LEICA CAMERA AG")
-            // (X (Typ 113) starts with "LEICA\0\0x7", Make is "LEICA CAMERA AG")
-
-            if ("LEICA\0\u0001\0".equals(firstEightChars) ||
-                "LEICA\0\u0004\0".equals(firstEightChars) ||
-                "LEICA\0\u0005\0".equals(firstEightChars) ||
-                "LEICA\0\u0006\0".equals(firstEightChars) ||
-                "LEICA\0\u0007\0".equals(firstEightChars))
-            {
-                pushDirectory(LeicaType5MakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, makernoteOffset);
-            } else if ("Leica Camera AG".equals(cameraMake)) {
-                pushDirectory(LeicaMakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset);
-            } else if ("LEICA".equals(cameraMake)) {
-                // Some Leica cameras use Panasonic makernote tags
-                pushDirectory(PanasonicMakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset);
-            } else {
-                return false;
-            }
-        } else if ("Panasonic\u0000\u0000\u0000".equals(firstTwelveChars)) {
-            // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
-            // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
-            // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
-            pushDirectory(PanasonicMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset);
-        } else if ("AOC\u0000".equals(firstFourChars)) {
-            // NON-Standard TIFF IFD Data using Casio Type 2 Tags
-            // IFD has no Next-IFD pointer at end of IFD, and
-            // Offsets are relative to the start of the current IFD tag, not the TIFF header
-            // Observed for:
-            // - Pentax ist D
-            pushDirectory(CasioType2MakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 6, makernoteOffset);
-        } else if (cameraMake != null && (cameraMake.toUpperCase().startsWith("PENTAX") || cameraMake.toUpperCase().startsWith("ASAHI"))) {
-            // NON-Standard TIFF IFD Data using Pentax Tags
-            // IFD has no Next-IFD pointer at end of IFD, and
-            // Offsets are relative to the start of the current IFD tag, not the TIFF header
-            // Observed for:
-            // - PENTAX Optio 330
-            // - PENTAX Optio 430
-            pushDirectory(PentaxMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, makernoteOffset);
-//        } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) {
-//            // This Konica data is not understood.  Header identified in accordance with information at this site:
-//            // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html
-//            // TODO add support for minolta/konica cameras
-//            exifDirectory.addError("Unsupported Konica/Minolta data ignored.");
-        } else if ("SANYO\0\1\0".equals(firstEightChars)) {
-            pushDirectory(SanyoMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, makernoteOffset);
-        } else if (cameraMake != null && cameraMake.toLowerCase().startsWith("ricoh")) {
-            if (firstTwoChars.equals("Rv") || firstThreeChars.equals("Rev")) {
-                // This is a textual format, where the makernote bytes look like:
-                //   Rv0103;Rg1C;Bg18;Ll0;Ld0;Aj0000;Bn0473800;Fp2E00:������������������������������
-                //   Rv0103;Rg1C;Bg18;Ll0;Ld0;Aj0000;Bn0473800;Fp2D05:������������������������������
-                //   Rv0207;Sf6C84;Rg76;Bg60;Gg42;Ll0;Ld0;Aj0004;Bn0B02900;Fp10B8;Md6700;Ln116900086D27;Sv263:0000000000000000000000��
-                // This format is currently unsupported
-                return false;
-            } else if (firstFiveChars.equalsIgnoreCase("Ricoh")) {
-                // Always in Motorola byte order
-                reader.setMotorolaByteOrder(true);
-                pushDirectory(RicohMakernoteDirectory.class);
-                TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, makernoteOffset);
-            }
-        } else if (firstTenChars.equals("Apple iOS\0")) {
-            // Always in Motorola byte order
-            boolean orderBefore = reader.isMotorolaByteOrder();
-            reader.setMotorolaByteOrder(true);
-            pushDirectory(AppleMakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset + 14, makernoteOffset);
-            reader.setMotorolaByteOrder(orderBefore);
-        } else if (reader.getUInt16(makernoteOffset) == ReconyxHyperFireMakernoteDirectory.MAKERNOTE_VERSION) {
-            ReconyxHyperFireMakernoteDirectory directory = new ReconyxHyperFireMakernoteDirectory();
-            _metadata.addDirectory(directory);
-            processReconyxHyperFireMakernote(directory, makernoteOffset, reader);
-        } else if (firstNineChars.equalsIgnoreCase("RECONYXUF")) {
-            ReconyxUltraFireMakernoteDirectory directory = new ReconyxUltraFireMakernoteDirectory();
-            _metadata.addDirectory(directory);
-            processReconyxUltraFireMakernote(directory, makernoteOffset, reader);
-        } else if ("SAMSUNG".equals(cameraMake)) {
-            // Only handles Type2 notes correctly. Others aren't implemented, and it's complex to determine which ones to use
-            pushDirectory(SamsungType2MakernoteDirectory.class);
-            TiffReader.processIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset);
-        } else {
-            // The makernote is not comprehended by this library.
-            // If you are reading this and believe a particular camera's image should be processed, get in touch.
-            return false;
-        }
-
-        reader.setMotorolaByteOrder(byteOrderBefore);
-        return true;
-    }
-
-    private static boolean handlePrintIM(@NotNull final Directory directory, final int tagId)
-    {
-        if (tagId == ExifDirectoryBase.TAG_PRINT_IMAGE_MATCHING_INFO)
-            return true;
-
-        if (tagId == 0x0E00) {
-            // Tempting to say every tagid of 0x0E00 is a PIM tag, but can't be 100% sure
-            if (directory instanceof CasioType2MakernoteDirectory ||
-                directory instanceof KyoceraMakernoteDirectory ||
-                directory instanceof NikonType2MakernoteDirectory ||
-                directory instanceof OlympusMakernoteDirectory ||
-                directory instanceof PanasonicMakernoteDirectory ||
-                directory instanceof PentaxMakernoteDirectory ||
-                directory instanceof RicohMakernoteDirectory ||
-                directory instanceof SanyoMakernoteDirectory ||
-                directory instanceof SonyType1MakernoteDirectory)
-                return true;
-        }
-
-        return false;
-    }
-
-    /// <summary>
-    /// Process PrintIM IFD
-    /// </summary>
-    /// <remarks>
-    /// Converted from Exiftool version 10.33 created by Phil Harvey
-    /// http://www.sno.phy.queensu.ca/~phil/exiftool/
-    /// lib\Image\ExifTool\PrintIM.pm
-    /// </remarks>
-    private static void processPrintIM(@NotNull final PrintIMDirectory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader, final int byteCount) throws IOException
-    {
-        Boolean resetByteOrder = null;
-
-        if (byteCount == 0) {
-            directory.addError("Empty PrintIM data");
-            return;
-        }
-
-        if (byteCount <= 15) {
-            directory.addError("Bad PrintIM data");
-            return;
-        }
-
-        String header = reader.getString(tagValueOffset, 12, Charsets.UTF_8);
-
-        if (!header.startsWith("PrintIM")) {
-            directory.addError("Invalid PrintIM header");
-            return;
-        }
-
-        // check size of PrintIM block
-        int num = reader.getUInt16(tagValueOffset + 14);
-
-        if (byteCount < 16 + num * 6) {
-            // size is too big, maybe byte ordering is wrong
-            resetByteOrder = reader.isMotorolaByteOrder();
-            reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder());
-            num = reader.getUInt16(tagValueOffset + 14);
-            if (byteCount < 16 + num * 6) {
-                directory.addError("Bad PrintIM size");
-                return;
-            }
-        }
-
-        directory.setObject(PrintIMDirectory.TagPrintImVersion, header.substring(8, 12));
-
-        for (int n = 0; n < num; n++) {
-            int pos = tagValueOffset + 16 + n * 6;
-            int tag = reader.getUInt16(pos);
-            long val = reader.getUInt32(pos + 2);
-
-            directory.setObject(tag, val);
-        }
-
-        if (resetByteOrder != null)
-            reader.setMotorolaByteOrder(resetByteOrder);
-    }
-
-    private static void processKodakMakernote(@NotNull final KodakMakernoteDirectory directory, final int tagValueOffset, @NotNull final RandomAccessReader reader)
-    {
-        // Kodak's makernote is not in IFD format. It has values at fixed offsets.
-        int dataOffset = tagValueOffset + 8;
-        try {
-            directory.setStringValue(KodakMakernoteDirectory.TAG_KODAK_MODEL, reader.getStringValue(dataOffset, 8, Charsets.UTF_8));
-            directory.setInt(KodakMakernoteDirectory.TAG_QUALITY, reader.getUInt8(dataOffset + 9));
-            directory.setInt(KodakMakernoteDirectory.TAG_BURST_MODE, reader.getUInt8(dataOffset + 10));
-            directory.setInt(KodakMakernoteDirectory.TAG_IMAGE_WIDTH, reader.getUInt16(dataOffset + 12));
-            directory.setInt(KodakMakernoteDirectory.TAG_IMAGE_HEIGHT, reader.getUInt16(dataOffset + 14));
-            directory.setInt(KodakMakernoteDirectory.TAG_YEAR_CREATED, reader.getUInt16(dataOffset + 16));
-            directory.setByteArray(KodakMakernoteDirectory.TAG_MONTH_DAY_CREATED, reader.getBytes(dataOffset + 18, 2));
-            directory.setByteArray(KodakMakernoteDirectory.TAG_TIME_CREATED, reader.getBytes(dataOffset + 20, 4));
-            directory.setInt(KodakMakernoteDirectory.TAG_BURST_MODE_2, reader.getUInt16(dataOffset + 24));
-            directory.setInt(KodakMakernoteDirectory.TAG_SHUTTER_MODE, reader.getUInt8(dataOffset + 27));
-            directory.setInt(KodakMakernoteDirectory.TAG_METERING_MODE, reader.getUInt8(dataOffset + 28));
-            directory.setInt(KodakMakernoteDirectory.TAG_SEQUENCE_NUMBER, reader.getUInt8(dataOffset + 29));
-            directory.setInt(KodakMakernoteDirectory.TAG_F_NUMBER, reader.getUInt16(dataOffset + 30));
-            directory.setLong(KodakMakernoteDirectory.TAG_EXPOSURE_TIME, reader.getUInt32(dataOffset + 32));
-            directory.setInt(KodakMakernoteDirectory.TAG_EXPOSURE_COMPENSATION, reader.getInt16(dataOffset + 36));
-            directory.setInt(KodakMakernoteDirectory.TAG_FOCUS_MODE, reader.getUInt8(dataOffset + 56));
-            directory.setInt(KodakMakernoteDirectory.TAG_WHITE_BALANCE, reader.getUInt8(dataOffset + 64));
-            directory.setInt(KodakMakernoteDirectory.TAG_FLASH_MODE, reader.getUInt8(dataOffset + 92));
-            directory.setInt(KodakMakernoteDirectory.TAG_FLASH_FIRED, reader.getUInt8(dataOffset + 93));
-            directory.setInt(KodakMakernoteDirectory.TAG_ISO_SETTING, reader.getUInt16(dataOffset + 94));
-            directory.setInt(KodakMakernoteDirectory.TAG_ISO, reader.getUInt16(dataOffset + 96));
-            directory.setInt(KodakMakernoteDirectory.TAG_TOTAL_ZOOM, reader.getUInt16(dataOffset + 98));
-            directory.setInt(KodakMakernoteDirectory.TAG_DATE_TIME_STAMP, reader.getUInt16(dataOffset + 100));
-            directory.setInt(KodakMakernoteDirectory.TAG_COLOR_MODE, reader.getUInt16(dataOffset + 102));
-            directory.setInt(KodakMakernoteDirectory.TAG_DIGITAL_ZOOM, reader.getUInt16(dataOffset + 104));
-            directory.setInt(KodakMakernoteDirectory.TAG_SHARPNESS, reader.getInt8(dataOffset + 107));
-        } catch (IOException ex) {
-            directory.addError("Error processing Kodak makernote data: " + ex.getMessage());
-        }
-    }
-
-    private static void processReconyxHyperFireMakernote(@NotNull final ReconyxHyperFireMakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
-    {
-        directory.setObject(ReconyxHyperFireMakernoteDirectory.TAG_MAKERNOTE_VERSION, reader.getUInt16(makernoteOffset));
-
-        int major = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION);
-        int minor = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION + 2);
-        int revision = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION + 4);
-        String buildYear = String.format("%04X", reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION + 6));
-        String buildDate = String.format("%04X", reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION + 8));
-        String buildYearAndDate = buildYear + buildDate;
-        Integer build;
-        try {
-            build = Integer.parseInt(buildYearAndDate);
-        } catch (NumberFormatException e) {
-            build = null;
-        }
-
-        if (build != null) {
-            directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d.%s", major, minor, revision, build));
-        } else {
-            directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d", major, minor, revision));
-            directory.addError("Error processing Reconyx HyperFire makernote data: build '" + buildYearAndDate + "' is not in the expected format and will be omitted from Firmware Version.");
-        }
-
-        directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_TRIGGER_MODE, String.valueOf((char)reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_TRIGGER_MODE)));
-        directory.setIntArray(ReconyxHyperFireMakernoteDirectory.TAG_SEQUENCE,
-                      new int[]
-                      {
-                          reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_SEQUENCE),
-                          reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_SEQUENCE + 2)
-                      });
-
-        int eventNumberHigh = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_EVENT_NUMBER);
-        int eventNumberLow = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_EVENT_NUMBER + 2);
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_EVENT_NUMBER, (eventNumberHigh << 16) + eventNumberLow);
-
-        int seconds = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL);
-        int minutes = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 2);
-        int hour = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 4);
-        int month = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 6);
-        int day = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 8);
-        int year = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 10);
-
-        if ((seconds >= 0 && seconds < 60) &&
-            (minutes >= 0 && minutes < 60) &&
-            (hour >= 0 && hour < 24) &&
-            (month >= 1 && month < 13) &&
-            (day >= 1 && day < 32) &&
-            (year >= 1 && year <= 9999)) {
-            directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL,
-                    String.format("%4d:%2d:%2d %2d:%2d:%2d", year, month, day, hour, minutes, seconds));
-        } else {
-            directory.addError("Error processing Reconyx HyperFire makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time.");
-        }
-
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_MOON_PHASE, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_MOON_PHASE));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, reader.getInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_AMBIENT_TEMPERATURE_FAHRENHEIT));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_AMBIENT_TEMPERATURE, reader.getInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_AMBIENT_TEMPERATURE));
-        //directory.setByteArray(ReconyxHyperFireMakernoteDirectory.TAG_SERIAL_NUMBER, reader.getBytes(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_SERIAL_NUMBER, 28));
-        directory.setStringValue(ReconyxHyperFireMakernoteDirectory.TAG_SERIAL_NUMBER, new StringValue(reader.getBytes(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_SERIAL_NUMBER, 28), Charsets.UTF_16LE));
-        // two unread bytes: the serial number's terminating null
-
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_CONTRAST, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_CONTRAST));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_BRIGHTNESS, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_BRIGHTNESS));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_SHARPNESS, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_SHARPNESS));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_SATURATION, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_SATURATION));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_INFRARED_ILLUMINATOR, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_INFRARED_ILLUMINATOR));
-        directory.setInt(ReconyxHyperFireMakernoteDirectory.TAG_MOTION_SENSITIVITY, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_MOTION_SENSITIVITY));
-        directory.setDouble(ReconyxHyperFireMakernoteDirectory.TAG_BATTERY_VOLTAGE, reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_BATTERY_VOLTAGE) / 1000.0);
-        directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, reader.getNullTerminatedString(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, 44, Charsets.UTF_8));
-    }
-
-    private static void processReconyxUltraFireMakernote(@NotNull final ReconyxUltraFireMakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
-    {
-        directory.setString(ReconyxUltraFireMakernoteDirectory.TAG_LABEL, reader.getString(makernoteOffset, 9, Charsets.UTF_8));
-        /*uint makernoteID = ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernoteID));
-        directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernoteID, makernoteID);
-        if (makernoteID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_ID) {
-            directory.addError("Error processing Reconyx UltraFire makernote data: unknown Makernote ID 0x" + makernoteID.ToString("x8"));
-            return;
-        }
-        directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernoteSize, ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernoteSize)));
-        uint makernotePublicID = ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernotePublicID));
-        directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernotePublicID, makernotePublicID);
-        if (makernotePublicID != ReconyxUltraFireMakernoteDirectory.MAKERNOTE_PUBLIC_ID) {
-            directory.addError("Error processing Reconyx UltraFire makernote data: unknown Makernote Public ID 0x" + makernotePublicID.ToString("x8"));
-            return;
-        }*/
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagMakernotePublicSize, ByteConvert.FromBigEndianToNative(reader.GetUInt16(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagMakernotePublicSize)));
-
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagCameraVersion, ProcessReconyxUltraFireVersion(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagCameraVersion, reader));
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagUibVersion, ProcessReconyxUltraFireVersion(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagUibVersion, reader));
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagBtlVersion, ProcessReconyxUltraFireVersion(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagBtlVersion, reader));
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagPexVersion, ProcessReconyxUltraFireVersion(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagPexVersion, reader));
-
-        directory.setString(ReconyxUltraFireMakernoteDirectory.TAG_EVENT_TYPE, reader.getString(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_EVENT_TYPE, 1, Charsets.UTF_8));
-        directory.setIntArray(ReconyxUltraFireMakernoteDirectory.TAG_SEQUENCE,
-                      new int[]
-                      {
-                          reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_SEQUENCE),
-                          reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_SEQUENCE + 1)
-                      });
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagEventNumber, ByteConvert.FromBigEndianToNative(reader.GetUInt32(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagEventNumber)));
-
-        byte seconds = reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL);
-        byte minutes = reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 1);
-        byte hour = reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 2);
-        byte day = reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 3);
-        byte month = reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 4);
-        /*ushort year = ByteConvert.FromBigEndianToNative(reader.GetUInt16(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagDateTimeOriginal + 5));
-        if ((seconds >= 0 && seconds < 60) &&
-            (minutes >= 0 && minutes < 60) &&
-            (hour >= 0 && hour < 24) &&
-            (month >= 1 && month < 13) &&
-            (day >= 1 && day < 32) &&
-            (year >= 1 && year <= 9999)) {
-            directory.Set(ReconyxUltraFireMakernoteDirectory.TAG_DATE_TIME_ORIGINAL, new DateTime(year, month, day, hour, minutes, seconds, DateTimeKind.Unspecified));
-        } else {
-            directory.addError("Error processing Reconyx UltraFire makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time.");
-        }*/
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagDayOfWeek, reader.GetByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagDayOfWeek));
-
-        directory.setInt(ReconyxUltraFireMakernoteDirectory.TAG_MOON_PHASE, reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_MOON_PHASE));
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagAmbientTemperatureFahrenheit, ByteConvert.FromBigEndianToNative(reader.GetInt16(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagAmbientTemperatureFahrenheit)));
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagAmbientTemperature, ByteConvert.FromBigEndianToNative(reader.GetInt16(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagAmbientTemperature)));
-
-        directory.setInt(ReconyxUltraFireMakernoteDirectory.TAG_FLASH, reader.getByte(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_FLASH));
-        //directory.Set(ReconyxUltraFireMakernoteDirectory.TagBatteryVoltage, ByteConvert.FromBigEndianToNative(reader.GetUInt16(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TagBatteryVoltage)) / 1000.0);
-        directory.setStringValue(ReconyxUltraFireMakernoteDirectory.TAG_SERIAL_NUMBER, new StringValue(reader.getBytes(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_SERIAL_NUMBER, 14), Charsets.UTF_8));
-        // unread byte: the serial number's terminating null
-        directory.setString(ReconyxUltraFireMakernoteDirectory.TAG_USER_LABEL, reader.getNullTerminatedString(makernoteOffset + ReconyxUltraFireMakernoteDirectory.TAG_USER_LABEL, 20, Charsets.UTF_8));
-    }
-}
-
Index: unk/src/com/drew/metadata/exif/GpsDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/GpsDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,338 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.GeoLocation;
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DecimalFormat;
-
-import static com.drew.metadata.exif.GpsDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link GpsDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class GpsDescriptor extends TagDescriptor<GpsDirectory>
-{
-    public GpsDescriptor(@NotNull GpsDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_VERSION_ID:
-                return getGpsVersionIdDescription();
-            case TAG_ALTITUDE:
-                return getGpsAltitudeDescription();
-            case TAG_ALTITUDE_REF:
-                return getGpsAltitudeRefDescription();
-            case TAG_STATUS:
-                return getGpsStatusDescription();
-            case TAG_MEASURE_MODE:
-                return getGpsMeasureModeDescription();
-            case TAG_DOP:
-                return getGpsDopDescription();
-            case TAG_SPEED_REF:
-                return getGpsSpeedRefDescription();
-            case TAG_SPEED:
-                return getGpsSpeedDescription();
-            case TAG_TRACK_REF:
-            case TAG_IMG_DIRECTION_REF:
-            case TAG_DEST_BEARING_REF:
-                return getGpsDirectionReferenceDescription(tagType);
-            case TAG_TRACK:
-            case TAG_IMG_DIRECTION:
-            case TAG_DEST_BEARING:
-                return getGpsDirectionDescription(tagType);
-            case TAG_DEST_LATITUDE:
-                return getGpsDestLatitudeDescription();
-            case TAG_DEST_LONGITUDE:
-                return getGpsDestLongitudeDescription();
-            case TAG_DEST_DISTANCE_REF:
-                return getGpsDestinationReferenceDescription();
-            case TAG_DEST_DISTANCE:
-                return getGpsDestDistanceDescription();
-            case TAG_TIME_STAMP:
-                return getGpsTimeStampDescription();
-            case TAG_LONGITUDE:
-                // three rational numbers -- displayed in HH"MM"SS.ss
-                return getGpsLongitudeDescription();
-            case TAG_LATITUDE:
-                // three rational numbers -- displayed in HH"MM"SS.ss
-                return getGpsLatitudeDescription();
-            case TAG_PROCESSING_METHOD:
-                return getGpsProcessingMethodDescription();
-            case TAG_AREA_INFORMATION:
-                return getGpsAreaInformationDescription();
-            case TAG_DIFFERENTIAL:
-                return getGpsDifferentialDescription();
-            case TAG_H_POSITIONING_ERROR:
-                return getGpsHPositioningErrorDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    private String getGpsVersionIdDescription()
-    {
-        return getVersionBytesDescription(TAG_VERSION_ID, 1);
-    }
-
-    @Nullable
-    public String getGpsLatitudeDescription()
-    {
-        GeoLocation location = _directory.getGeoLocation();
-        return location == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude());
-    }
-
-    @Nullable
-    public String getGpsLongitudeDescription()
-    {
-        GeoLocation location = _directory.getGeoLocation();
-        return location == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude());
-    }
-
-    @Nullable
-    public String getGpsTimeStampDescription()
-    {
-        // time in hour, min, sec
-        Rational[] timeComponents = _directory.getRationalArray(TAG_TIME_STAMP);
-        DecimalFormat df = new DecimalFormat("00.000");
-        return timeComponents == null
-            ? null
-            : String.format("%02d:%02d:%s UTC",
-                timeComponents[0].intValue(),
-                timeComponents[1].intValue(),
-                df.format(timeComponents[2].doubleValue()));
-    }
-
-    @Nullable
-    public String getGpsDestLatitudeDescription()
-    {
-        Rational[] latitudes = _directory.getRationalArray(TAG_DEST_LATITUDE);
-        String latitudeRef = _directory.getString(TAG_DEST_LATITUDE_REF);
-
-        if (latitudes == null || latitudes.length != 3 || latitudeRef == null)
-            return null;
-
-        Double lat = GeoLocation.degreesMinutesSecondsToDecimal(
-            latitudes[0], latitudes[1], latitudes[2], latitudeRef.equalsIgnoreCase("S"));
-
-        return lat == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(lat);
-    }
-
-    @Nullable
-    public String getGpsDestLongitudeDescription()
-    {
-        Rational[] longitudes = _directory.getRationalArray(TAG_LONGITUDE);
-        String longitudeRef = _directory.getString(TAG_LONGITUDE_REF);
-
-        if (longitudes == null || longitudes.length != 3 || longitudeRef == null)
-            return null;
-
-        Double lon = GeoLocation.degreesMinutesSecondsToDecimal(
-            longitudes[0], longitudes[1], longitudes[2], longitudeRef.equalsIgnoreCase("W"));
-
-        return lon == null ? null : GeoLocation.decimalToDegreesMinutesSecondsString(lon);
-    }
-
-    @Nullable
-    public String getGpsDestinationReferenceDescription()
-    {
-        final String value = _directory.getString(TAG_DEST_DISTANCE_REF);
-        if (value == null)
-            return null;
-        String distanceRef = value.trim();
-        if ("K".equalsIgnoreCase(distanceRef)) {
-            return "kilometers";
-        } else if ("M".equalsIgnoreCase(distanceRef)) {
-            return "miles";
-        } else if ("N".equalsIgnoreCase(distanceRef)) {
-            return "knots";
-        } else {
-            return "Unknown (" + distanceRef + ")";
-        }
-    }
-
-    @Nullable
-    public String getGpsDestDistanceDescription()
-    {
-        final Rational value = _directory.getRational(TAG_DEST_DISTANCE);
-        if (value == null)
-            return null;
-        final String unit = getGpsDestinationReferenceDescription();
-        return String.format("%s %s",
-            new DecimalFormat("0.##").format(value.doubleValue()),
-            unit == null ? "unit" : unit.toLowerCase());
-    }
-
-    @Nullable
-    public String getGpsDirectionDescription(int tagType)
-    {
-        Rational angle = _directory.getRational(tagType);
-        // provide a decimal version of rational numbers in the description, to avoid strings like "35334/199 degrees"
-        String value = angle != null
-            ? new DecimalFormat("0.##").format(angle.doubleValue())
-            : _directory.getString(tagType);
-        return value == null || value.trim().length() == 0 ? null : value.trim() + " degrees";
-    }
-
-    @Nullable
-    public String getGpsDirectionReferenceDescription(int tagType)
-    {
-        final String value = _directory.getString(tagType);
-        if (value == null)
-            return null;
-        String gpsDistRef = value.trim();
-        if ("T".equalsIgnoreCase(gpsDistRef)) {
-            return "True direction";
-        } else if ("M".equalsIgnoreCase(gpsDistRef)) {
-            return "Magnetic direction";
-        } else {
-            return "Unknown (" + gpsDistRef + ")";
-        }
-    }
-
-    @Nullable
-    public String getGpsDopDescription()
-    {
-        final Rational value = _directory.getRational(TAG_DOP);
-        return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue());
-    }
-
-    @Nullable
-    public String getGpsSpeedRefDescription()
-    {
-        final String value = _directory.getString(TAG_SPEED_REF);
-        if (value == null)
-            return null;
-        String gpsSpeedRef = value.trim();
-        if ("K".equalsIgnoreCase(gpsSpeedRef)) {
-            return "km/h";
-        } else if ("M".equalsIgnoreCase(gpsSpeedRef)) {
-            return "mph";
-        } else if ("N".equalsIgnoreCase(gpsSpeedRef)) {
-            return "knots";
-        } else {
-            return "Unknown (" + gpsSpeedRef + ")";
-        }
-    }
-
-    @Nullable
-    public String getGpsSpeedDescription()
-    {
-        final Rational value = _directory.getRational(TAG_SPEED);
-        if (value == null)
-            return null;
-        final String unit = getGpsSpeedRefDescription();
-        return String.format("%s %s",
-            new DecimalFormat("0.##").format(value.doubleValue()),
-            unit == null ? "unit" : unit.toLowerCase());
-    }
-
-    @Nullable
-    public String getGpsMeasureModeDescription()
-    {
-        final String value = _directory.getString(TAG_MEASURE_MODE);
-        if (value == null)
-            return null;
-        String gpsSpeedMeasureMode = value.trim();
-        if ("2".equalsIgnoreCase(gpsSpeedMeasureMode)) {
-            return "2-dimensional measurement";
-        } else if ("3".equalsIgnoreCase(gpsSpeedMeasureMode)) {
-            return "3-dimensional measurement";
-        } else {
-            return "Unknown (" + gpsSpeedMeasureMode + ")";
-        }
-    }
-
-    @Nullable
-    public String getGpsStatusDescription()
-    {
-        final String value = _directory.getString(TAG_STATUS);
-        if (value == null)
-            return null;
-        String gpsStatus = value.trim();
-        if ("A".equalsIgnoreCase(gpsStatus)) {
-            return "Active (Measurement in progress)";
-        } else if ("V".equalsIgnoreCase(gpsStatus)) {
-            return "Void (Measurement Interoperability)";
-        } else {
-            return "Unknown (" + gpsStatus + ")";
-        }
-    }
-
-    @Nullable
-    public String getGpsAltitudeRefDescription()
-    {
-        return getIndexedDescription(TAG_ALTITUDE_REF, "Sea level", "Below sea level");
-    }
-
-    @Nullable
-    public String getGpsAltitudeDescription()
-    {
-        final Rational value = _directory.getRational(TAG_ALTITUDE);
-        return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()) + " metres";
-    }
-
-    @Nullable
-    public String getGpsProcessingMethodDescription()
-    {
-        return getEncodedTextDescription(TAG_PROCESSING_METHOD);
-    }
-
-    @Nullable
-    public String getGpsAreaInformationDescription()
-    {
-        return getEncodedTextDescription(TAG_AREA_INFORMATION);
-    }
-
-    @Nullable
-    public String getGpsDifferentialDescription()
-    {
-        return getIndexedDescription(TAG_DIFFERENTIAL, "No Correction", "Differential Corrected");
-    }
-
-    @Nullable
-    public String getGpsHPositioningErrorDescription()
-    {
-        final Rational value = _directory.getRational(TAG_H_POSITIONING_ERROR);
-        return value == null ? null : new DecimalFormat("0.##").format(value.doubleValue()) + " metres";
-    }
-
-    @Nullable
-    public String getDegreesMinutesSecondsDescription()
-    {
-        GeoLocation location = _directory.getGeoLocation();
-        return location == null ? null : location.toDMSString();
-    }
-}
Index: unk/src/com/drew/metadata/exif/GpsDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/GpsDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,227 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif;
-
-import com.drew.lang.GeoLocation;
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Locale;
-
-/**
- * Describes Exif tags that contain Global Positioning System (GPS) data.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class GpsDirectory extends ExifDirectoryBase
-{
-    /** GPS tag version GPSVersionID 0 0 BYTE 4 */
-    public static final int TAG_VERSION_ID = 0x0000;
-    /** North or South Latitude GPSLatitudeRef 1 1 ASCII 2 */
-    public static final int TAG_LATITUDE_REF = 0x0001;
-    /** Latitude GPSLatitude 2 2 RATIONAL 3 */
-    public static final int TAG_LATITUDE = 0x0002;
-    /** East or West Longitude GPSLongitudeRef 3 3 ASCII 2 */
-    public static final int TAG_LONGITUDE_REF = 0x0003;
-    /** Longitude GPSLongitude 4 4 RATIONAL 3 */
-    public static final int TAG_LONGITUDE = 0x0004;
-    /** Altitude reference GPSAltitudeRef 5 5 BYTE 1 */
-    public static final int TAG_ALTITUDE_REF = 0x0005;
-    /** Altitude GPSAltitude 6 6 RATIONAL 1 */
-    public static final int TAG_ALTITUDE = 0x0006;
-    /** GPS time (atomic clock) GPSTimeStamp 7 7 RATIONAL 3 */
-    public static final int TAG_TIME_STAMP = 0x0007;
-    /** GPS satellites used for measurement GPSSatellites 8 8 ASCII Any */
-    public static final int TAG_SATELLITES = 0x0008;
-    /** GPS receiver status GPSStatus 9 9 ASCII 2 */
-    public static final int TAG_STATUS = 0x0009;
-    /** GPS measurement mode GPSMeasureMode 10 A ASCII 2 */
-    public static final int TAG_MEASURE_MODE = 0x000A;
-    /** Measurement precision GPSDOP 11 B RATIONAL 1 */
-    public static final int TAG_DOP = 0x000B;
-    /** Speed unit GPSSpeedRef 12 C ASCII 2 */
-    public static final int TAG_SPEED_REF = 0x000C;
-    /** Speed of GPS receiver GPSSpeed 13 D RATIONAL 1 */
-    public static final int TAG_SPEED = 0x000D;
-    /** Reference for direction of movement GPSTrackRef 14 E ASCII 2 */
-    public static final int TAG_TRACK_REF = 0x000E;
-    /** Direction of movement GPSTrack 15 F RATIONAL 1 */
-    public static final int TAG_TRACK = 0x000F;
-    /** Reference for direction of image GPSImgDirectionRef 16 10 ASCII 2 */
-    public static final int TAG_IMG_DIRECTION_REF = 0x0010;
-    /** Direction of image GPSImgDirection 17 11 RATIONAL 1 */
-    public static final int TAG_IMG_DIRECTION = 0x0011;
-    /** Geodetic survey data used GPSMapDatum 18 12 ASCII Any */
-    public static final int TAG_MAP_DATUM = 0x0012;
-    /** Reference for latitude of destination GPSDestLatitudeRef 19 13 ASCII 2 */
-    public static final int TAG_DEST_LATITUDE_REF = 0x0013;
-    /** Latitude of destination GPSDestLatitude 20 14 RATIONAL 3 */
-    public static final int TAG_DEST_LATITUDE = 0x0014;
-    /** Reference for longitude of destination GPSDestLongitudeRef 21 15 ASCII 2 */
-    public static final int TAG_DEST_LONGITUDE_REF = 0x0015;
-    /** Longitude of destination GPSDestLongitude 22 16 RATIONAL 3 */
-    public static final int TAG_DEST_LONGITUDE = 0x0016;
-    /** Reference for bearing of destination GPSDestBearingRef 23 17 ASCII 2 */
-    public static final int TAG_DEST_BEARING_REF = 0x0017;
-    /** Bearing of destination GPSDestBearing 24 18 RATIONAL 1 */
-    public static final int TAG_DEST_BEARING = 0x0018;
-    /** Reference for distance to destination GPSDestDistanceRef 25 19 ASCII 2 */
-    public static final int TAG_DEST_DISTANCE_REF = 0x0019;
-    /** Distance to destination GPSDestDistance 26 1A RATIONAL 1 */
-    public static final int TAG_DEST_DISTANCE = 0x001A;
-    /** Name of the method used for location finding GPSProcessingMethod 27 1B UNDEFINED Any */
-    public static final int TAG_PROCESSING_METHOD = 0x001B;
-    /** Name of the GPS area GPSAreaInformation 28 1C UNDEFINED Any */
-    public static final int TAG_AREA_INFORMATION = 0x001C;
-    /** Date and time GPSDateStamp 29 1D ASCII 11 */
-    public static final int TAG_DATE_STAMP = 0x001D;
-    /** Whether differential correction is applied GPSDifferential 30 1E SHORT 1 */
-    public static final int TAG_DIFFERENTIAL = 0x001E;
-    /** Horizontal positioning errors GPSHPositioningError 31 1F RATIONAL 1 */
-    public static final int TAG_H_POSITIONING_ERROR = 0x001F;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        addExifTagNames(_tagNameMap);
-
-        _tagNameMap.put(TAG_VERSION_ID, "GPS Version ID");
-        _tagNameMap.put(TAG_LATITUDE_REF, "GPS Latitude Ref");
-        _tagNameMap.put(TAG_LATITUDE, "GPS Latitude");
-        _tagNameMap.put(TAG_LONGITUDE_REF, "GPS Longitude Ref");
-        _tagNameMap.put(TAG_LONGITUDE, "GPS Longitude");
-        _tagNameMap.put(TAG_ALTITUDE_REF, "GPS Altitude Ref");
-        _tagNameMap.put(TAG_ALTITUDE, "GPS Altitude");
-        _tagNameMap.put(TAG_TIME_STAMP, "GPS Time-Stamp");
-        _tagNameMap.put(TAG_SATELLITES, "GPS Satellites");
-        _tagNameMap.put(TAG_STATUS, "GPS Status");
-        _tagNameMap.put(TAG_MEASURE_MODE, "GPS Measure Mode");
-        _tagNameMap.put(TAG_DOP, "GPS DOP");
-        _tagNameMap.put(TAG_SPEED_REF, "GPS Speed Ref");
-        _tagNameMap.put(TAG_SPEED, "GPS Speed");
-        _tagNameMap.put(TAG_TRACK_REF, "GPS Track Ref");
-        _tagNameMap.put(TAG_TRACK, "GPS Track");
-        _tagNameMap.put(TAG_IMG_DIRECTION_REF, "GPS Img Direction Ref");
-        _tagNameMap.put(TAG_IMG_DIRECTION, "GPS Img Direction");
-        _tagNameMap.put(TAG_MAP_DATUM, "GPS Map Datum");
-        _tagNameMap.put(TAG_DEST_LATITUDE_REF, "GPS Dest Latitude Ref");
-        _tagNameMap.put(TAG_DEST_LATITUDE, "GPS Dest Latitude");
-        _tagNameMap.put(TAG_DEST_LONGITUDE_REF, "GPS Dest Longitude Ref");
-        _tagNameMap.put(TAG_DEST_LONGITUDE, "GPS Dest Longitude");
-        _tagNameMap.put(TAG_DEST_BEARING_REF, "GPS Dest Bearing Ref");
-        _tagNameMap.put(TAG_DEST_BEARING, "GPS Dest Bearing");
-        _tagNameMap.put(TAG_DEST_DISTANCE_REF, "GPS Dest Distance Ref");
-        _tagNameMap.put(TAG_DEST_DISTANCE, "GPS Dest Distance");
-        _tagNameMap.put(TAG_PROCESSING_METHOD, "GPS Processing Method");
-        _tagNameMap.put(TAG_AREA_INFORMATION, "GPS Area Information");
-        _tagNameMap.put(TAG_DATE_STAMP, "GPS Date Stamp");
-        _tagNameMap.put(TAG_DIFFERENTIAL, "GPS Differential");
-        _tagNameMap.put(TAG_H_POSITIONING_ERROR, "GPS H Positioning Error");
-    }
-
-    public GpsDirectory()
-    {
-        this.setDescriptor(new GpsDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "GPS";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    /**
-     * Parses various tags in an attempt to obtain a single object representing the latitude and longitude
-     * at which this image was captured.
-     *
-     * @return The geographical location of this image, if possible, otherwise null
-     */
-    @Nullable
-    public GeoLocation getGeoLocation()
-    {
-        Rational[] latitudes = getRationalArray(TAG_LATITUDE);
-        Rational[] longitudes = getRationalArray(TAG_LONGITUDE);
-        String latitudeRef = getString(TAG_LATITUDE_REF);
-        String longitudeRef = getString(TAG_LONGITUDE_REF);
-
-        // Make sure we have the required values
-        if (latitudes == null || latitudes.length != 3)
-            return null;
-        if (longitudes == null || longitudes.length != 3)
-            return null;
-        if (latitudeRef == null || longitudeRef == null)
-            return null;
-
-        Double lat = GeoLocation.degreesMinutesSecondsToDecimal(latitudes[0], latitudes[1], latitudes[2], latitudeRef.equalsIgnoreCase("S"));
-        Double lon = GeoLocation.degreesMinutesSecondsToDecimal(longitudes[0], longitudes[1], longitudes[2], longitudeRef.equalsIgnoreCase("W"));
-
-        // This can return null, in cases where the conversion was not possible
-        if (lat == null || lon == null)
-            return null;
-
-        return new GeoLocation(lat, lon);
-    }
-
-    /**
-     * Parses the date stamp tag and the time stamp tag to obtain a single Date object representing the
-     * date and time when this image was captured.
-     *
-     * @return A Date object representing when this image was captured, if possible, otherwise null
-     */
-    @Nullable
-    public Date getGpsDate()
-    {
-        String date = getString(TAG_DATE_STAMP);
-        Rational[] timeComponents = getRationalArray(TAG_TIME_STAMP);
-
-        // Make sure we have the required values
-        if (date == null)
-            return null;
-        if (timeComponents == null || timeComponents.length != 3)
-            return null;
-
-        String dateTime = String.format(Locale.US, "%s %02d:%02d:%02.3f UTC",
-            date, timeComponents[0].intValue(), timeComponents[1].intValue(), timeComponents[2].doubleValue());
-        try {
-            DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss.S z");
-            return parser.parse(dateTime);
-        } catch (ParseException e) {
-            return null;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawDistortionDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawDistortionDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,159 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.PanasonicRawDistortionDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PanasonicRawDistortionDirectory}.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawDistortionDescriptor extends TagDescriptor<PanasonicRawDistortionDirectory>
-{
-    public PanasonicRawDistortionDescriptor(@NotNull PanasonicRawDistortionDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagDistortionParam02:
-                return getDistortionParam02Description();
-            case TagDistortionParam04:
-                return getDistortionParam04Description();
-            case TagDistortionScale:
-                return getDistortionScaleDescription();
-            case TagDistortionCorrection:
-                return getDistortionCorrectionDescription();
-            case TagDistortionParam08:
-                return getDistortionParam08Description();
-            case TagDistortionParam09:
-                return getDistortionParam09Description();
-            case TagDistortionParam11:
-                return getDistortionParam11Description();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getWbTypeDescription(int tagType)
-    {
-        Integer wbtype = _directory.getInteger(tagType);
-        if (wbtype == null)
-            return null;
-
-        return super.getLightSourceDescription(wbtype.shortValue());
-    }
-
-    @Nullable
-    public String getDistortionParam02Description()
-    {
-        Integer value = _directory.getInteger(TagDistortionParam02);
-        if (value == null)
-            return null;
-
-        return new Rational(value, 32678).toString();
-    }
-
-    @Nullable
-    public String getDistortionParam04Description()
-    {
-        Integer value = _directory.getInteger(TagDistortionParam04);
-        if (value == null)
-            return null;
-
-        return new Rational(value, 32678).toString();
-    }
-
-    @Nullable
-    public String getDistortionScaleDescription()
-    {
-        Integer value = _directory.getInteger(TagDistortionScale);
-        if (value == null)
-            return null;
-
-        //return (1 / (1 + value / 32768)).toString();
-        return Integer.toString(1 / (1 + value / 32768));
-    }
-
-    @Nullable
-    public String getDistortionCorrectionDescription()
-    {
-        Integer value = _directory.getInteger(TagDistortionCorrection);
-        if (value == null)
-            return null;
-
-        // (have seen the upper 4 bits set for GF5 and GX1, giving a value of -4095 - PH)
-        int mask = 0x000f;
-        switch (value & mask)
-        {
-            case 0:
-                return "Off";
-            case 1:
-                return "On";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getDistortionParam08Description()
-    {
-        Integer value = _directory.getInteger(TagDistortionParam08);
-        if (value == null)
-            return null;
-
-        return new Rational(value, 32678).toString();
-    }
-
-    @Nullable
-    public String getDistortionParam09Description()
-    {
-        Integer value = _directory.getInteger(TagDistortionParam09);
-        if (value == null)
-            return null;
-
-        return new Rational(value, 32678).toString();
-    }
-
-    @Nullable
-    public String getDistortionParam11Description()
-    {
-        Integer value = _directory.getInteger(TagDistortionParam11);
-        if (value == null)
-            return null;
-
-        return new Rational(value, 32678).toString();
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawDistortionDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawDistortionDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,86 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * These tags can be found in Panasonic/Leica RAW, RW2 and RWL images. The index values are 'fake' but
- * chosen specifically to make processing easier
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawDistortionDirectory extends Directory
-{
-    // 0 and 1 are checksums
-
-    public static final int TagDistortionParam02 = 2;
-
-    public static final int TagDistortionParam04 = 4;
-    public static final int TagDistortionScale = 5;
-
-    public static final int TagDistortionCorrection = 7;
-    public static final int TagDistortionParam08 = 8;
-    public static final int TagDistortionParam09 = 9;
-
-    public static final int TagDistortionParam11 = 11;
-    public static final int TagDistortionN = 12;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagDistortionParam02, "Distortion Param 2");
-        _tagNameMap.put(TagDistortionParam04, "Distortion Param 4");
-        _tagNameMap.put(TagDistortionScale, "Distortion Scale");
-        _tagNameMap.put(TagDistortionCorrection, "Distortion Correction");
-        _tagNameMap.put(TagDistortionParam08, "Distortion Param 8");
-        _tagNameMap.put(TagDistortionParam09, "Distortion Param 9");
-        _tagNameMap.put(TagDistortionParam11, "Distortion Param 11");
-        _tagNameMap.put(TagDistortionN, "Distortion N");
-    }
-
-    public PanasonicRawDistortionDirectory()
-    {
-        this.setDescriptor(new PanasonicRawDistortionDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "PanasonicRaw DistortionInfo";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawIFD0Descriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawIFD0Descriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,58 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.PanasonicRawIFD0Directory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PanasonicRawIFD0Directory}.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawIFD0Descriptor extends TagDescriptor<PanasonicRawIFD0Directory>
-{
-    public PanasonicRawIFD0Descriptor(@NotNull PanasonicRawIFD0Directory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType)
-        {
-            case TagPanasonicRawVersion:
-                return getVersionBytesDescription(TagPanasonicRawVersion, 2);
-            case TagOrientation:
-                return getOrientationDescription(TagOrientation);
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawIFD0Directory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawIFD0Directory.java	(revision 16024)
+++ 	(revision )
@@ -1,153 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * These tags are found in IFD0 of Panasonic/Leica RAW, RW2 and RWL images.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawIFD0Directory extends Directory
-{
-    public static final int TagPanasonicRawVersion = 0x0001;
-    public static final int TagSensorWidth = 0x0002;
-    public static final int TagSensorHeight = 0x0003;
-    public static final int TagSensorTopBorder = 0x0004;
-    public static final int TagSensorLeftBorder = 0x0005;
-    public static final int TagSensorBottomBorder = 0x0006;
-    public static final int TagSensorRightBorder = 0x0007;
-
-    public static final int TagBlackLevel1 = 0x0008;
-    public static final int TagBlackLevel2 = 0x0009;
-    public static final int TagBlackLevel3 = 0x000a;
-    public static final int TagLinearityLimitRed = 0x000e;
-    public static final int TagLinearityLimitGreen = 0x000f;
-    public static final int TagLinearityLimitBlue = 0x0010;
-    public static final int TagRedBalance = 0x0011;
-    public static final int TagBlueBalance = 0x0012;
-    public static final int TagWbInfo = 0x0013;
-
-    public static final int TagIso = 0x0017;
-    public static final int TagHighIsoMultiplierRed = 0x0018;
-    public static final int TagHighIsoMultiplierGreen = 0x0019;
-    public static final int TagHighIsoMultiplierBlue = 0x001a;
-    public static final int TagBlackLevelRed = 0x001c;
-    public static final int TagBlackLevelGreen = 0x001d;
-    public static final int TagBlackLevelBlue = 0x001e;
-    public static final int TagWbRedLevel = 0x0024;
-    public static final int TagWbGreenLevel = 0x0025;
-    public static final int TagWbBlueLevel = 0x0026;
-
-    public static final int TagWbInfo2 = 0x0027;
-
-    public static final int TagJpgFromRaw = 0x002e;
-
-    public static final int TagCropTop = 0x002f;
-    public static final int TagCropLeft = 0x0030;
-    public static final int TagCropBottom = 0x0031;
-    public static final int TagCropRight = 0x0032;
-
-    public static final int TagMake = 0x010f;
-    public static final int TagModel = 0x0110;
-    public static final int TagStripOffsets = 0x0111;
-    public static final int TagOrientation = 0x0112;
-    public static final int TagRowsPerStrip = 0x0116;
-    public static final int TagStripByteCounts = 0x0117;
-    public static final int TagRawDataOffset = 0x0118;
-
-    public static final int TagDistortionInfo = 0x0119;
-
-    public PanasonicRawIFD0Directory()
-    {
-        this.setDescriptor(new PanasonicRawIFD0Descriptor(this));
-    }
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagPanasonicRawVersion, "Panasonic Raw Version");
-        _tagNameMap.put(TagSensorWidth, "Sensor Width");
-        _tagNameMap.put(TagSensorHeight, "Sensor Height");
-        _tagNameMap.put(TagSensorTopBorder, "Sensor Top Border");
-        _tagNameMap.put(TagSensorLeftBorder, "Sensor Left Border");
-        _tagNameMap.put(TagSensorBottomBorder, "Sensor Bottom Border");
-        _tagNameMap.put(TagSensorRightBorder, "Sensor Right Border");
-
-        _tagNameMap.put(TagBlackLevel1, "Black Level 1");
-        _tagNameMap.put(TagBlackLevel2, "Black Level 2");
-        _tagNameMap.put(TagBlackLevel3, "Black Level 3");
-        _tagNameMap.put(TagLinearityLimitRed, "Linearity Limit Red");
-        _tagNameMap.put(TagLinearityLimitGreen, "Linearity Limit Green");
-        _tagNameMap.put(TagLinearityLimitBlue, "Linearity Limit Blue");
-        _tagNameMap.put(TagRedBalance, "Red Balance");
-        _tagNameMap.put(TagBlueBalance, "Blue Balance");
-
-        _tagNameMap.put(TagIso, "ISO");
-        _tagNameMap.put(TagHighIsoMultiplierRed, "High ISO Multiplier Red");
-        _tagNameMap.put(TagHighIsoMultiplierGreen, "High ISO Multiplier Green");
-        _tagNameMap.put(TagHighIsoMultiplierBlue, "High ISO Multiplier Blue");
-        _tagNameMap.put(TagBlackLevelRed, "Black Level Red");
-        _tagNameMap.put(TagBlackLevelGreen, "Black Level Green");
-        _tagNameMap.put(TagBlackLevelBlue, "Black Level Blue");
-        _tagNameMap.put(TagWbRedLevel, "WB Red Level");
-        _tagNameMap.put(TagWbGreenLevel, "WB Green Level");
-        _tagNameMap.put(TagWbBlueLevel, "WB Blue Level");
-
-        _tagNameMap.put(TagJpgFromRaw, "Jpg From Raw");
-
-        _tagNameMap.put(TagCropTop, "Crop Top");
-        _tagNameMap.put(TagCropLeft, "Crop Left");
-        _tagNameMap.put(TagCropBottom, "Crop Bottom");
-        _tagNameMap.put(TagCropRight, "Crop Right");
-
-        _tagNameMap.put(TagMake, "Make");
-        _tagNameMap.put(TagModel, "Model");
-        _tagNameMap.put(TagStripOffsets, "Strip Offsets");
-        _tagNameMap.put(TagOrientation, "Orientation");
-        _tagNameMap.put(TagRowsPerStrip, "Rows Per Strip");
-        _tagNameMap.put(TagStripByteCounts, "Strip Byte Counts");
-        _tagNameMap.put(TagRawDataOffset, "Raw Data Offset");
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "PanasonicRaw Exif IFD0";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawWbInfo2Descriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawWbInfo2Descriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,71 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.PanasonicRawWbInfo2Directory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PanasonicRawWbInfo2Directory}.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawWbInfo2Descriptor extends TagDescriptor<PanasonicRawWbInfo2Directory>
-{
-    public PanasonicRawWbInfo2Descriptor(@NotNull PanasonicRawWbInfo2Directory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagWbType1:
-            case TagWbType2:
-            case TagWbType3:
-            case TagWbType4:
-            case TagWbType5:
-            case TagWbType6:
-            case TagWbType7:
-                return getWbTypeDescription(tagType);
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getWbTypeDescription(int tagType)
-    {
-        Integer wbtype = _directory.getInteger(tagType);
-        if (wbtype == null)
-            return null;
-
-        return super.getLightSourceDescription(wbtype.shortValue());
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawWbInfo2Directory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawWbInfo2Directory.java	(revision 16024)
+++ 	(revision )
@@ -1,103 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * These tags can be found in Panasonic/Leica RAW, RW2 and RWL images. The index values are 'fake' but
- * chosen specifically to make processing easier
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawWbInfo2Directory extends Directory
-{
-    public static final int TagNumWbEntries = 0;
-
-    public static final int TagWbType1 = 1;
-    public static final int TagWbRgbLevels1 = 2;
-
-    public static final int TagWbType2 = 5;
-    public static final int TagWbRgbLevels2 = 6;
-
-    public static final int TagWbType3 = 9;
-    public static final int TagWbRgbLevels3 = 10;
-
-    public static final int TagWbType4 = 13;
-    public static final int TagWbRgbLevels4 = 14;
-
-    public static final int TagWbType5 = 17;
-    public static final int TagWbRgbLevels5 = 18;
-
-    public static final int TagWbType6 = 21;
-    public static final int TagWbRgbLevels6 = 22;
-
-    public static final int TagWbType7 = 25;
-    public static final int TagWbRgbLevels7 = 26;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagNumWbEntries, "Num WB Entries");
-        _tagNameMap.put(TagNumWbEntries, "Num WB Entries");
-        _tagNameMap.put(TagWbType1, "WB Type 1");
-        _tagNameMap.put(TagWbRgbLevels1, "WB RGB Levels 1");
-        _tagNameMap.put(TagWbType2, "WB Type 2");
-        _tagNameMap.put(TagWbRgbLevels2, "WB RGB Levels 2");
-        _tagNameMap.put(TagWbType3, "WB Type 3");
-        _tagNameMap.put(TagWbRgbLevels3, "WB RGB Levels 3");
-        _tagNameMap.put(TagWbType4, "WB Type 4");
-        _tagNameMap.put(TagWbRgbLevels4, "WB RGB Levels 4");
-        _tagNameMap.put(TagWbType5, "WB Type 5");
-        _tagNameMap.put(TagWbRgbLevels5, "WB RGB Levels 5");
-        _tagNameMap.put(TagWbType6, "WB Type 6");
-        _tagNameMap.put(TagWbRgbLevels6, "WB RGB Levels 6");
-        _tagNameMap.put(TagWbType7, "WB Type 7");
-        _tagNameMap.put(TagWbRgbLevels7, "WB RGB Levels 7");
-    }
-
-    public PanasonicRawWbInfo2Directory()
-    {
-        this.setDescriptor(new PanasonicRawWbInfo2Descriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "PanasonicRaw WbInfo2";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawWbInfoDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawWbInfoDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,71 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.PanasonicRawWbInfoDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PanasonicRawWbInfoDirectory}.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawWbInfoDescriptor extends TagDescriptor<PanasonicRawWbInfoDirectory>
-{
-    public PanasonicRawWbInfoDescriptor(@NotNull PanasonicRawWbInfoDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagWbType1:
-            case TagWbType2:
-            case TagWbType3:
-            case TagWbType4:
-            case TagWbType5:
-            case TagWbType6:
-            case TagWbType7:
-                return getWbTypeDescription(tagType);
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getWbTypeDescription(int tagType)
-    {
-        Integer wbtype = _directory.getInteger(tagType);
-        if (wbtype == null)
-            return null;
-
-        return super.getLightSourceDescription(wbtype.shortValue());
-    }
-}
Index: unk/src/com/drew/metadata/exif/PanasonicRawWbInfoDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PanasonicRawWbInfoDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,102 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * These tags can be found in Panasonic/Leica RAW, RW2 and RWL images. The index values are 'fake' but
- * chosen specifically to make processing easier
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicRawWbInfoDirectory extends Directory
-{
-    public static final int TagNumWbEntries = 0;
-
-    public static final int TagWbType1 = 1;
-    public static final int TagWbRbLevels1 = 2;
-
-    public static final int TagWbType2 = 4;
-    public static final int TagWbRbLevels2 = 5;
-
-    public static final int TagWbType3 = 7;
-    public static final int TagWbRbLevels3 = 8;
-
-    public static final int TagWbType4 = 10;
-    public static final int TagWbRbLevels4 = 11;
-
-    public static final int TagWbType5 = 13;
-    public static final int TagWbRbLevels5 = 14;
-
-    public static final int TagWbType6 = 16;
-    public static final int TagWbRbLevels6 = 17;
-
-    public static final int TagWbType7 = 19;
-    public static final int TagWbRbLevels7 = 20;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagNumWbEntries, "Num WB Entries");
-        _tagNameMap.put(TagWbType1, "WB Type 1");
-        _tagNameMap.put(TagWbRbLevels1, "WB RGB Levels 1");
-        _tagNameMap.put(TagWbType2, "WB Type 2");
-        _tagNameMap.put(TagWbRbLevels2, "WB RGB Levels 2");
-        _tagNameMap.put(TagWbType3, "WB Type 3");
-        _tagNameMap.put(TagWbRbLevels3, "WB RGB Levels 3");
-        _tagNameMap.put(TagWbType4, "WB Type 4");
-        _tagNameMap.put(TagWbRbLevels4, "WB RGB Levels 4");
-        _tagNameMap.put(TagWbType5, "WB Type 5");
-        _tagNameMap.put(TagWbRbLevels5, "WB RGB Levels 5");
-        _tagNameMap.put(TagWbType6, "WB Type 6");
-        _tagNameMap.put(TagWbRbLevels6, "WB RGB Levels 6");
-        _tagNameMap.put(TagWbType7, "WB Type 7");
-        _tagNameMap.put(TagWbRbLevels7, "WB RGB Levels 7");
-    }
-
-    public PanasonicRawWbInfoDirectory()
-    {
-        this.setDescriptor(new PanasonicRawWbInfoDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "PanasonicRaw WbInfo";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/PrintIMDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PrintIMDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,58 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.PrintIMDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PrintIMDirectory}.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PrintIMDescriptor extends TagDescriptor<PrintIMDirectory>
-{
-    public PrintIMDescriptor(@NotNull PrintIMDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagPrintImVersion:
-                return super.getDescription(tagType);
-            default:
-                Integer value = _directory.getInteger(tagType);
-                if (value == null)
-                    return null;
-                return String.format("0x%08x", value);
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/PrintIMDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/PrintIMDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * These tags can be found in Epson proprietary metadata. The index values are 'fake' but
- * chosen specifically to make processing easier
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PrintIMDirectory extends Directory
-{
-    public static final int TagPrintImVersion = 0x0000;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagPrintImVersion, "PrintIM Version");
-    }
-
-    public PrintIMDirectory()
-    {
-        this.setDescriptor(new PrintIMDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "PrintIM";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/AppleMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/AppleMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,59 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link AppleMakernoteDirectory}.
- * <p>
- * Using information from http://owl.phy.queensu.ca/~phil/exiftool/TagNames/Apple.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class AppleMakernoteDescriptor extends TagDescriptor<AppleMakernoteDirectory>
-{
-    public AppleMakernoteDescriptor(@NotNull AppleMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case AppleMakernoteDirectory.TAG_HDR_IMAGE_TYPE:
-                return getHdrImageTypeDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getHdrImageTypeDescription()
-    {
-        return getIndexedDescription(AppleMakernoteDirectory.TAG_HDR_IMAGE_TYPE, 3, "HDR Image", "Original Image");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/AppleMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/AppleMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,70 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Apple cameras.
- * <p>
- * Using information from http://owl.phy.queensu.ca/~phil/exiftool/TagNames/Apple.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class AppleMakernoteDirectory extends Directory
-{
-    public static final int TAG_RUN_TIME = 0x0003;
-    public static final int TAG_HDR_IMAGE_TYPE = 0x000a;
-    public static final int TAG_BURST_UUID = 0x000b;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_RUN_TIME, "Run Time");
-        _tagNameMap.put(TAG_HDR_IMAGE_TYPE, "HDR Image Type");
-        _tagNameMap.put(TAG_BURST_UUID, "Burst UUID");
-    }
-
-    public AppleMakernoteDirectory()
-    {
-        this.setDescriptor(new AppleMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Apple Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,1155 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DecimalFormat;
-import java.util.HashMap;
-
-import static com.drew.metadata.exif.makernotes.CanonMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link CanonMakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class CanonMakernoteDescriptor extends TagDescriptor<CanonMakernoteDirectory>
-{
-    public CanonMakernoteDescriptor(@NotNull CanonMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_CANON_SERIAL_NUMBER:
-                return getSerialNumberDescription();
-            case CameraSettings.TAG_FLASH_ACTIVITY:
-                return getFlashActivityDescription();
-            case CameraSettings.TAG_FOCUS_TYPE:
-                return getFocusTypeDescription();
-            case CameraSettings.TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case CameraSettings.TAG_RECORD_MODE:
-                return getRecordModeDescription();
-            case CameraSettings.TAG_QUALITY:
-                return getQualityDescription();
-            case CameraSettings.TAG_MACRO_MODE:
-                return getMacroModeDescription();
-            case CameraSettings.TAG_SELF_TIMER_DELAY:
-                return getSelfTimerDelayDescription();
-            case CameraSettings.TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            case CameraSettings.TAG_CONTINUOUS_DRIVE_MODE:
-                return getContinuousDriveModeDescription();
-            case CameraSettings.TAG_FOCUS_MODE_1:
-                return getFocusMode1Description();
-            case CameraSettings.TAG_IMAGE_SIZE:
-                return getImageSizeDescription();
-            case CameraSettings.TAG_EASY_SHOOTING_MODE:
-                return getEasyShootingModeDescription();
-            case CameraSettings.TAG_CONTRAST:
-                return getContrastDescription();
-            case CameraSettings.TAG_SATURATION:
-                return getSaturationDescription();
-            case CameraSettings.TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case CameraSettings.TAG_ISO:
-                return getIsoDescription();
-            case CameraSettings.TAG_METERING_MODE:
-                return getMeteringModeDescription();
-            case CameraSettings.TAG_AF_POINT_SELECTED:
-                return getAfPointSelectedDescription();
-            case CameraSettings.TAG_EXPOSURE_MODE:
-                return getExposureModeDescription();
-            case CameraSettings.TAG_LENS_TYPE:
-                return getLensTypeDescription();
-            case CameraSettings.TAG_LONG_FOCAL_LENGTH:
-                return getLongFocalLengthDescription();
-            case CameraSettings.TAG_SHORT_FOCAL_LENGTH:
-                return getShortFocalLengthDescription();
-            case CameraSettings.TAG_FOCAL_UNITS_PER_MM:
-                return getFocalUnitsPerMillimetreDescription();
-            case CameraSettings.TAG_FLASH_DETAILS:
-                return getFlashDetailsDescription();
-            case CameraSettings.TAG_FOCUS_MODE_2:
-                return getFocusMode2Description();
-            case FocalLength.TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case FocalLength.TAG_AF_POINT_USED:
-                return getAfPointUsedDescription();
-            case FocalLength.TAG_FLASH_BIAS:
-                return getFlashBiasDescription();
-            case AFInfo.TAG_AF_POINTS_IN_FOCUS:
-                return getTagAfPointsInFocus();
-            case CameraSettings.TAG_MAX_APERTURE:
-                return getMaxApertureDescription();
-            case CameraSettings.TAG_MIN_APERTURE:
-                return getMinApertureDescription();
-            case CameraSettings.TAG_FOCUS_CONTINUOUS:
-                return getFocusContinuousDescription();
-            case CameraSettings.TAG_AE_SETTING:
-                return getAESettingDescription();
-            case CanonMakernoteDirectory.CameraSettings.TAG_DISPLAY_APERTURE:
-                return getDisplayApertureDescription();
-            case CanonMakernoteDirectory.CameraSettings.TAG_SPOT_METERING_MODE:
-                return getSpotMeteringModeDescription();
-            case CanonMakernoteDirectory.CameraSettings.TAG_PHOTO_EFFECT:
-                return getPhotoEffectDescription();
-            case CanonMakernoteDirectory.CameraSettings.TAG_MANUAL_FLASH_OUTPUT:
-                return getManualFlashOutputDescription();
-            case CanonMakernoteDirectory.CameraSettings.TAG_COLOR_TONE:
-                return getColorToneDescription();
-            case CanonMakernoteDirectory.CameraSettings.TAG_SRAW_QUALITY:
-                return getSRawQualityDescription();
-
-            // It turns out that these values are dependent upon the camera model and therefore the below code was
-            // incorrect for some Canon models.  This needs to be revisited.
-
-//            case TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION:
-//                return getLongExposureNoiseReductionDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS:
-//                return getShutterAutoExposureLockButtonDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP:
-//                return getMirrorLockupDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL:
-//                return getTvAndAvExposureLevelDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT:
-//                return getAutoFocusAssistLightDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE:
-//                return getShutterSpeedInAvModeDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_BRACKETING:
-//                return getAutoExposureBracketingSequenceAndAutoCancellationDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC:
-//                return getShutterCurtainSyncDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_AF_STOP:
-//                return getLensAutoFocusStopButtonDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION:
-//                return getFillFlashReductionDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN:
-//                return getMenuButtonReturnPositionDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION:
-//                return getSetButtonFunctionWhenShootingDescription();
-//            case TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING:
-//                return getSensorCleaningDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getSerialNumberDescription()
-    {
-        // http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
-        Integer value = _directory.getInteger(TAG_CANON_SERIAL_NUMBER);
-        if (value == null)
-            return null;
-        return String.format("%04X%05d", (value >> 8) & 0xFF, value & 0xFF);
-    }
-
-/*
-    @Nullable
-    public String getLongExposureNoiseReductionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Off";
-            case 1:     return "On";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getShutterAutoExposureLockButtonDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "AF/AE lock";
-            case 1:     return "AE lock/AF";
-            case 2:     return "AF/AF lock";
-            case 3:     return "AE+release/AE+AF";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getMirrorLockupDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Disabled";
-            case 1:     return "Enabled";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getTvAndAvExposureLevelDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "1/2 stop";
-            case 1:     return "1/3 stop";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAutoFocusAssistLightDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "On (Auto)";
-            case 1:     return "Off";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getShutterSpeedInAvModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Automatic";
-            case 1:     return "1/200 (fixed)";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAutoExposureBracketingSequenceAndAutoCancellationDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_BRACKETING);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "0,-,+ / Enabled";
-            case 1:     return "0,-,+ / Disabled";
-            case 2:     return "-,0,+ / Enabled";
-            case 3:     return "-,0,+ / Disabled";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getShutterCurtainSyncDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "1st Curtain Sync";
-            case 1:     return "2nd Curtain Sync";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getLensAutoFocusStopButtonDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_AF_STOP);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "AF stop";
-            case 1:     return "Operate AF";
-            case 2:     return "Lock AE and start timer";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFillFlashReductionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Enabled";
-            case 1:     return "Disabled";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getMenuButtonReturnPositionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Top";
-            case 1:     return "Previous (volatile)";
-            case 2:     return "Previous";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSetButtonFunctionWhenShootingDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Not Assigned";
-            case 1:     return "Change Quality";
-            case 2:     return "Change ISO Speed";
-            case 3:     return "Select Parameters";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSensorCleaningDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0:     return "Disabled";
-            case 1:     return "Enabled";
-            default:    return "Unknown (" + value + ")";
-        }
-    }
-*/
-
-    @Nullable
-    public String getFlashBiasDescription()
-    {
-        Integer value = _directory.getInteger(FocalLength.TAG_FLASH_BIAS);
-
-        if (value == null)
-            return null;
-
-        boolean isNegative = false;
-        if (value > 0xF000) {
-            isNegative = true;
-            value = 0xFFFF - value;
-            value++;
-        }
-
-        // this tag is interesting in that the values returned are:
-        //  0, 0.375, 0.5, 0.626, 1
-        // not
-        //  0, 0.33,  0.5, 0.66,  1
-
-        return (isNegative ? "-" : "") + Float.toString(value / 32f) + " EV";
-    }
-
-    @Nullable
-    public String getAfPointUsedDescription()
-    {
-        Integer value = _directory.getInteger(FocalLength.TAG_AF_POINT_USED);
-        if (value == null)
-            return null;
-        if ((value & 0x7) == 0) {
-            return "Right";
-        } else if ((value & 0x7) == 1) {
-            return "Centre";
-        } else if ((value & 0x7) == 2) {
-            return "Left";
-        } else {
-            return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getTagAfPointsInFocus()
-    {
-        Integer value = _directory.getInteger(AFInfo.TAG_AF_POINTS_IN_FOCUS);
-        if (value == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-
-        for (int i = 0; i < 16; i++)
-        {
-            if ((value & 1 << i) != 0)
-            {
-                if (sb.length() != 0)
-                    sb.append(',');
-                sb.append(i);
-            }
-        }
-
-        return sb.length() == 0 ? "None" : sb.toString();
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(
-            FocalLength.TAG_WHITE_BALANCE,
-            "Auto",
-            "Sunny",
-            "Cloudy",
-            "Tungsten",
-            "Florescent",
-            "Flash",
-            "Custom"
-        );
-    }
-
-    @Nullable
-    public String getFocusMode2Description()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FOCUS_MODE_2, "Single", "Continuous");
-    }
-
-    @Nullable
-    public String getFlashDetailsDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_FLASH_DETAILS);
-        if (value == null)
-            return null;
-        if (((value >> 14) & 1) != 0) {
-            return "External E-TTL";
-        }
-        if (((value >> 13) & 1) != 0) {
-            return "Internal flash";
-        }
-        if (((value >> 11) & 1) != 0) {
-            return "FP sync used";
-        }
-        if (((value >> 4) & 1) != 0) {
-            return "FP sync enabled";
-        }
-        return "Unknown (" + value + ")";
-    }
-
-    @Nullable
-    public String getFocalUnitsPerMillimetreDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_FOCAL_UNITS_PER_MM);
-        if (value == null)
-            return null;
-        if (value != 0) {
-            return Integer.toString(value);
-        } else {
-            return "";
-        }
-    }
-
-    @Nullable
-    public String getShortFocalLengthDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_SHORT_FOCAL_LENGTH);
-        if (value == null)
-            return null;
-        String units = getFocalUnitsPerMillimetreDescription();
-        return Integer.toString(value) + " " + units;
-    }
-
-    @Nullable
-    public String getLongFocalLengthDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_LONG_FOCAL_LENGTH);
-        if (value == null)
-            return null;
-        String units = getFocalUnitsPerMillimetreDescription();
-        return Integer.toString(value) + " " + units;
-    }
-
-    @Nullable
-    public String getExposureModeDescription()
-    {
-        return getIndexedDescription(
-            CameraSettings.TAG_EXPOSURE_MODE,
-            "Easy shooting",
-            "Program",
-            "Tv-priority",
-            "Av-priority",
-            "Manual",
-            "A-DEP"
-        );
-    }
-
-    @Nullable
-    public String getLensTypeDescription() {
-        Integer value = _directory.getInteger(CameraSettings.TAG_LENS_TYPE);
-        if (value == null)
-            return null;
-
-        return _lensTypeById.containsKey(value)
-            ? _lensTypeById.get(value)
-            : String.format("Unknown (%d)", value);
-    }
-
-    @Nullable
-    public String getMaxApertureDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_MAX_APERTURE);
-        if (value == null)
-            return null;
-        if (value > 512)
-            return String.format("Unknown (%d)", value);
-        return getFStopDescription(Math.exp(decodeCanonEv(value) * Math.log(2.0) / 2.0));
-    }
-
-    @Nullable
-    public String getMinApertureDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_MIN_APERTURE);
-        if (value == null)
-            return null;
-        if (value > 512)
-            return String.format("Unknown (%d)", value);
-        return getFStopDescription(Math.exp(decodeCanonEv(value) * Math.log(2.0) / 2.0));
-    }
-
-    @Nullable
-    public String getAfPointSelectedDescription()
-    {
-        return getIndexedDescription(
-            CameraSettings.TAG_AF_POINT_SELECTED,
-            0x3000,
-            "None (MF)",
-            "Auto selected",
-            "Right",
-            "Centre",
-            "Left"
-        );
-    }
-
-    @Nullable
-    public String getMeteringModeDescription()
-    {
-        return getIndexedDescription(
-            CameraSettings.TAG_METERING_MODE,
-            3,
-            "Evaluative",
-            "Partial",
-            "Centre weighted"
-        );
-    }
-
-    @Nullable
-    public String getIsoDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_ISO);
-        if (value == null)
-            return null;
-
-        // Canon PowerShot S3 is special
-        int canonMask = 0x4000;
-        if ((value & canonMask) != 0)
-            return "" + (value & ~canonMask);
-
-        switch (value) {
-            case 0:
-                return "Not specified (see ISOSpeedRatings tag)";
-            case 15:
-                return "Auto";
-            case 16:
-                return "50";
-            case 17:
-                return "100";
-            case 18:
-                return "200";
-            case 19:
-                return "400";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_SHARPNESS);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0xFFFF:
-                return "Low";
-            case 0x000:
-                return "Normal";
-            case 0x001:
-                return "High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSaturationDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_SATURATION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0xFFFF:
-                return "Low";
-            case 0x000:
-                return "Normal";
-            case 0x001:
-                return "High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_CONTRAST);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0xFFFF:
-                return "Low";
-            case 0x000:
-                return "Normal";
-            case 0x001:
-                return "High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getEasyShootingModeDescription()
-    {
-        return getIndexedDescription(
-            CameraSettings.TAG_EASY_SHOOTING_MODE,
-            "Full auto",
-            "Manual",
-            "Landscape",
-            "Fast shutter",
-            "Slow shutter",
-            "Night",
-            "B&W",
-            "Sepia",
-            "Portrait",
-            "Sports",
-            "Macro / Closeup",
-            "Pan focus"
-        );
-    }
-
-    @Nullable
-    public String getImageSizeDescription()
-    {
-        return getIndexedDescription(
-            CameraSettings.TAG_IMAGE_SIZE,
-            "Large",
-            "Medium",
-            "Small"
-        );
-    }
-
-    @Nullable
-    public String getFocusMode1Description()
-    {
-        return getIndexedDescription(
-            CameraSettings.TAG_FOCUS_MODE_1,
-            "One-shot",
-            "AI Servo",
-            "AI Focus",
-            "Manual Focus",
-            // TODO should check field 32 here (FOCUS_MODE_2)
-            "Single",
-            "Continuous",
-            "Manual Focus"
-        );
-    }
-
-    @Nullable
-    public String getContinuousDriveModeDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_CONTINUOUS_DRIVE_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0:
-                final Integer delay = _directory.getInteger(CameraSettings.TAG_SELF_TIMER_DELAY);
-                if (delay != null)
-                    return delay == 0 ? "Single shot" : "Single shot with self-timer";
-            case 1:
-                return "Continuous";
-        }
-        return "Unknown (" + value + ")";
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_FLASH_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0:
-                return "No flash fired";
-            case 1:
-                return "Auto";
-            case 2:
-                return "On";
-            case 3:
-                return "Red-eye reduction";
-            case 4:
-                return "Slow-synchro";
-            case 5:
-                return "Auto and red-eye reduction";
-            case 6:
-                return "On and red-eye reduction";
-            case 16:
-                // note: this value not set on Canon D30
-                return "External flash";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSelfTimerDelayDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_SELF_TIMER_DELAY);
-        if (value == null)
-            return null;
-        if (value == 0) {
-            return "Self timer not used";
-        } else {
-            DecimalFormat format = new DecimalFormat("0.##");
-            return format.format((double)value * 0.1d) + " sec";
-        }
-    }
-
-    @Nullable
-    public String getMacroModeDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_MACRO_MODE, 1, "Macro", "Normal");
-    }
-
-    @Nullable
-    public String getQualityDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_QUALITY, 2, "Normal", "Fine", null, "Superfine");
-    }
-
-    @Nullable
-    public String getDigitalZoomDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_DIGITAL_ZOOM, "No digital zoom", "2x", "4x");
-    }
-
-    @Nullable
-    public String getRecordModeDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_RECORD_MODE, 1, "JPEG", "CRW+THM", "AVI+THM", "TIF", "TIF+JPEG", "CR2", "CR2+JPEG", null, "MOV", "MP4");
-    }
-
-    @Nullable
-    public String getFocusTypeDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_FOCUS_TYPE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0:
-                return "Manual";
-            case 1:
-                return "Auto";
-            case 3:
-                return "Close-up (Macro)";
-            case 8:
-                return "Locked (Pan Mode)";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFlashActivityDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FLASH_ACTIVITY, "Flash did not fire", "Flash fired");
-    }
-
-    @Nullable
-    public String getFocusContinuousDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FOCUS_CONTINUOUS, 0,
-            "Single", "Continuous", null, null, null, null, null, null, "Manual");
-    }
-
-    @Nullable
-    public String getAESettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_AE_SETTING, 0,
-            "Normal AE", "Exposure Compensation", "AE Lock", "AE Lock + Exposure Comp.", "No AE");
-    }
-
-    @Nullable
-    public String getDisplayApertureDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_DISPLAY_APERTURE);
-        if (value == null)
-            return null;
-
-        if (value == 0xFFFF)
-            return value.toString();
-        return getFStopDescription(value / 10f);
-    }
-
-    @Nullable
-    public String getSpotMeteringModeDescription()
-    {
-        return getIndexedDescription(CanonMakernoteDirectory.CameraSettings.TAG_SPOT_METERING_MODE, 0,
-            "Center", "AF Point");
-    }
-
-    @Nullable
-    public String getPhotoEffectDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_PHOTO_EFFECT);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 0:
-                return "Off";
-            case 1:
-                return "Vivid";
-            case 2:
-                return "Neutral";
-            case 3:
-                return "Smooth";
-            case 4:
-                return "Sepia";
-            case 5:
-                return "B&W";
-            case 6:
-                return "Custom";
-            case 100:
-                return "My Color Data";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getManualFlashOutputDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_MANUAL_FLASH_OUTPUT);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 0:
-                return "n/a";
-            case 0x500:
-                return "Full";
-            case 0x502:
-                return "Medium";
-            case 0x504:
-                return "Low";
-            case 0x7fff:
-                return "n/a";   // (EOS models)
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getColorToneDescription()
-    {
-        Integer value = _directory.getInteger(CameraSettings.TAG_COLOR_TONE);
-        if (value == null)
-            return null;
-
-        return value == 0x7fff ? "n/a" : value.toString();
-    }
-
-    @Nullable
-    public String getSRawQualityDescription()
-    {
-        return getIndexedDescription(CanonMakernoteDirectory.CameraSettings.TAG_SRAW_QUALITY, 0, "n/a", "sRAW1 (mRAW)", "sRAW2 (sRAW)");
-    }
-
-    /**
-     * Canon hex-based EV (modulo 0x20) to real number.
-     *
-     * Converted from Exiftool version 10.10 created by Phil Harvey
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/
-     * lib\Image\ExifTool\Canon.pm
-     *
-     *         eg) 0x00 -> 0
-     *             0x0c -> 0.33333
-     *             0x10 -> 0.5
-     *             0x14 -> 0.66666
-     *             0x20 -> 1   ... etc
-     */
-    private double decodeCanonEv(int val)
-    {
-        int sign = 1;
-        if (val < 0)
-        {
-            val = -val;
-            sign = -1;
-        }
-
-        int frac = val & 0x1f;
-        val -= frac;
-
-        if (frac == 0x0c)
-            frac = 0x20 / 3;
-        else if (frac == 0x14)
-            frac = 0x40 / 3;
-
-        return sign * (val + frac) / (double)0x20;
-    }
-
-    /**
-     *  Map from <see cref="CanonMakernoteDirectory.CameraSettings.TagLensType"/> to string descriptions.
-     *
-     *  Data sourced from http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html#LensType
-     *
-     *  Note that only Canon lenses are listed. Lenses from other manufacturers may identify themselves to the camera
-     *  as being from this set, but in fact may be quite different. This limits the usefulness of this data,
-     *  unfortunately.
-     */
-    private static final HashMap<Integer, String> _lensTypeById = new HashMap<Integer, String>();
-
-    static {
-        _lensTypeById.put(1, "Canon EF 50mm f/1.8");
-        _lensTypeById.put(2, "Canon EF 28mm f/2.8");
-        _lensTypeById.put(3, "Canon EF 135mm f/2.8 Soft");
-        _lensTypeById.put(4, "Canon EF 35-105mm f/3.5-4.5 or Sigma Lens");
-        _lensTypeById.put(5, "Canon EF 35-70mm f/3.5-4.5");
-        _lensTypeById.put(6, "Canon EF 28-70mm f/3.5-4.5 or Sigma or Tokina Lens");
-        _lensTypeById.put(7, "Canon EF 100-300mm f/5.6L");
-        _lensTypeById.put(8, "Canon EF 100-300mm f/5.6 or Sigma or Tokina Lens");
-        _lensTypeById.put(9, "Canon EF 70-210mm f/4");
-        _lensTypeById.put(10, "Canon EF 50mm f/2.5 Macro or Sigma Lens");
-        _lensTypeById.put(11, "Canon EF 35mm f/2");
-        _lensTypeById.put(13, "Canon EF 15mm f/2.8 Fisheye");
-        _lensTypeById.put(14, "Canon EF 50-200mm f/3.5-4.5L");
-        _lensTypeById.put(15, "Canon EF 50-200mm f/3.5-4.5");
-        _lensTypeById.put(16, "Canon EF 35-135mm f/3.5-4.5");
-        _lensTypeById.put(17, "Canon EF 35-70mm f/3.5-4.5A");
-        _lensTypeById.put(18, "Canon EF 28-70mm f/3.5-4.5");
-        _lensTypeById.put(20, "Canon EF 100-200mm f/4.5A");
-        _lensTypeById.put(21, "Canon EF 80-200mm f/2.8L");
-        _lensTypeById.put(22, "Canon EF 20-35mm f/2.8L or Tokina Lens");
-        _lensTypeById.put(23, "Canon EF 35-105mm f/3.5-4.5");
-        _lensTypeById.put(24, "Canon EF 35-80mm f/4-5.6 Power Zoom");
-        _lensTypeById.put(25, "Canon EF 35-80mm f/4-5.6 Power Zoom");
-        _lensTypeById.put(26, "Canon EF 100mm f/2.8 Macro or Other Lens");
-        _lensTypeById.put(27, "Canon EF 35-80mm f/4-5.6");
-        _lensTypeById.put(28, "Canon EF 80-200mm f/4.5-5.6 or Tamron Lens");
-        _lensTypeById.put(29, "Canon EF 50mm f/1.8 II");
-        _lensTypeById.put(30, "Canon EF 35-105mm f/4.5-5.6");
-        _lensTypeById.put(31, "Canon EF 75-300mm f/4-5.6 or Tamron Lens");
-        _lensTypeById.put(32, "Canon EF 24mm f/2.8 or Sigma Lens");
-        _lensTypeById.put(33, "Voigtlander or Carl Zeiss Lens");
-        _lensTypeById.put(35, "Canon EF 35-80mm f/4-5.6");
-        _lensTypeById.put(36, "Canon EF 38-76mm f/4.5-5.6");
-        _lensTypeById.put(37, "Canon EF 35-80mm f/4-5.6 or Tamron Lens");
-        _lensTypeById.put(38, "Canon EF 80-200mm f/4.5-5.6");
-        _lensTypeById.put(39, "Canon EF 75-300mm f/4-5.6");
-        _lensTypeById.put(40, "Canon EF 28-80mm f/3.5-5.6");
-        _lensTypeById.put(41, "Canon EF 28-90mm f/4-5.6");
-        _lensTypeById.put(42, "Canon EF 28-200mm f/3.5-5.6 or Tamron Lens");
-        _lensTypeById.put(43, "Canon EF 28-105mm f/4-5.6");
-        _lensTypeById.put(44, "Canon EF 90-300mm f/4.5-5.6");
-        _lensTypeById.put(45, "Canon EF-S 18-55mm f/3.5-5.6 [II]");
-        _lensTypeById.put(46, "Canon EF 28-90mm f/4-5.6");
-        _lensTypeById.put(47, "Zeiss Milvus 35mm f/2 or 50mm f/2");
-        _lensTypeById.put(48, "Canon EF-S 18-55mm f/3.5-5.6 IS");
-        _lensTypeById.put(49, "Canon EF-S 55-250mm f/4-5.6 IS");
-        _lensTypeById.put(50, "Canon EF-S 18-200mm f/3.5-5.6 IS");
-        _lensTypeById.put(51, "Canon EF-S 18-135mm f/3.5-5.6 IS");
-        _lensTypeById.put(52, "Canon EF-S 18-55mm f/3.5-5.6 IS II");
-        _lensTypeById.put(53, "Canon EF-S 18-55mm f/3.5-5.6 III");
-        _lensTypeById.put(54, "Canon EF-S 55-250mm f/4-5.6 IS II");
-        _lensTypeById.put(94, "Canon TS-E 17mm f/4L");
-        _lensTypeById.put(95, "Canon TS-E 24.0mm f/3.5 L II");
-        _lensTypeById.put(124, "Canon MP-E 65mm f/2.8 1-5x Macro Photo");
-        _lensTypeById.put(125, "Canon TS-E 24mm f/3.5L");
-        _lensTypeById.put(126, "Canon TS-E 45mm f/2.8");
-        _lensTypeById.put(127, "Canon TS-E 90mm f/2.8");
-        _lensTypeById.put(129, "Canon EF 300mm f/2.8L");
-        _lensTypeById.put(130, "Canon EF 50mm f/1.0L");
-        _lensTypeById.put(131, "Canon EF 28-80mm f/2.8-4L or Sigma Lens");
-        _lensTypeById.put(132, "Canon EF 1200mm f/5.6L");
-        _lensTypeById.put(134, "Canon EF 600mm f/4L IS");
-        _lensTypeById.put(135, "Canon EF 200mm f/1.8L");
-        _lensTypeById.put(136, "Canon EF 300mm f/2.8L");
-        _lensTypeById.put(137, "Canon EF 85mm f/1.2L or Sigma or Tamron Lens");
-        _lensTypeById.put(138, "Canon EF 28-80mm f/2.8-4L");
-        _lensTypeById.put(139, "Canon EF 400mm f/2.8L");
-        _lensTypeById.put(140, "Canon EF 500mm f/4.5L");
-        _lensTypeById.put(141, "Canon EF 500mm f/4.5L");
-        _lensTypeById.put(142, "Canon EF 300mm f/2.8L IS");
-        _lensTypeById.put(143, "Canon EF 500mm f/4L IS or Sigma Lens");
-        _lensTypeById.put(144, "Canon EF 35-135mm f/4-5.6 USM");
-        _lensTypeById.put(145, "Canon EF 100-300mm f/4.5-5.6 USM");
-        _lensTypeById.put(146, "Canon EF 70-210mm f/3.5-4.5 USM");
-        _lensTypeById.put(147, "Canon EF 35-135mm f/4-5.6 USM");
-        _lensTypeById.put(148, "Canon EF 28-80mm f/3.5-5.6 USM");
-        _lensTypeById.put(149, "Canon EF 100mm f/2 USM");
-        _lensTypeById.put(150, "Canon EF 14mm f/2.8L or Sigma Lens");
-        _lensTypeById.put(151, "Canon EF 200mm f/2.8L");
-        _lensTypeById.put(152, "Canon EF 300mm f/4L IS or Sigma Lens");
-        _lensTypeById.put(153, "Canon EF 35-350mm f/3.5-5.6L or Sigma or Tamron Lens");
-        _lensTypeById.put(154, "Canon EF 20mm f/2.8 USM or Zeiss Lens");
-        _lensTypeById.put(155, "Canon EF 85mm f/1.8 USM");
-        _lensTypeById.put(156, "Canon EF 28-105mm f/3.5-4.5 USM or Tamron Lens");
-        _lensTypeById.put(160, "Canon EF 20-35mm f/3.5-4.5 USM or Tamron or Tokina Lens");
-        _lensTypeById.put(161, "Canon EF 28-70mm f/2.8L or Sigma or Tamron Lens");
-        _lensTypeById.put(162, "Canon EF 200mm f/2.8L");
-        _lensTypeById.put(163, "Canon EF 300mm f/4L");
-        _lensTypeById.put(164, "Canon EF 400mm f/5.6L");
-        _lensTypeById.put(165, "Canon EF 70-200mm f/2.8 L");
-        _lensTypeById.put(166, "Canon EF 70-200mm f/2.8 L + 1.4x");
-        _lensTypeById.put(167, "Canon EF 70-200mm f/2.8 L + 2x");
-        _lensTypeById.put(168, "Canon EF 28mm f/1.8 USM or Sigma Lens");
-        _lensTypeById.put(169, "Canon EF 17-35mm f/2.8L or Sigma Lens");
-        _lensTypeById.put(170, "Canon EF 200mm f/2.8L II");
-        _lensTypeById.put(171, "Canon EF 300mm f/4L");
-        _lensTypeById.put(172, "Canon EF 400mm f/5.6L or Sigma Lens");
-        _lensTypeById.put(173, "Canon EF 180mm Macro f/3.5L or Sigma Lens");
-        _lensTypeById.put(174, "Canon EF 135mm f/2L or Other Lens");
-        _lensTypeById.put(175, "Canon EF 400mm f/2.8L");
-        _lensTypeById.put(176, "Canon EF 24-85mm f/3.5-4.5 USM");
-        _lensTypeById.put(177, "Canon EF 300mm f/4L IS");
-        _lensTypeById.put(178, "Canon EF 28-135mm f/3.5-5.6 IS");
-        _lensTypeById.put(179, "Canon EF 24mm f/1.4L");
-        _lensTypeById.put(180, "Canon EF 35mm f/1.4L or Other Lens");
-        _lensTypeById.put(181, "Canon EF 100-400mm f/4.5-5.6L IS + 1.4x or Sigma Lens");
-        _lensTypeById.put(182, "Canon EF 100-400mm f/4.5-5.6L IS + 2x or Sigma Lens");
-        _lensTypeById.put(183, "Canon EF 100-400mm f/4.5-5.6L IS or Sigma Lens");
-        _lensTypeById.put(184, "Canon EF 400mm f/2.8L + 2x");
-        _lensTypeById.put(185, "Canon EF 600mm f/4L IS");
-        _lensTypeById.put(186, "Canon EF 70-200mm f/4L");
-        _lensTypeById.put(187, "Canon EF 70-200mm f/4L + 1.4x");
-        _lensTypeById.put(188, "Canon EF 70-200mm f/4L + 2x");
-        _lensTypeById.put(189, "Canon EF 70-200mm f/4L + 2.8x");
-        _lensTypeById.put(190, "Canon EF 100mm f/2.8 Macro USM");
-        _lensTypeById.put(191, "Canon EF 400mm f/4 DO IS");
-        _lensTypeById.put(193, "Canon EF 35-80mm f/4-5.6 USM");
-        _lensTypeById.put(194, "Canon EF 80-200mm f/4.5-5.6 USM");
-        _lensTypeById.put(195, "Canon EF 35-105mm f/4.5-5.6 USM");
-        _lensTypeById.put(196, "Canon EF 75-300mm f/4-5.6 USM");
-        _lensTypeById.put(197, "Canon EF 75-300mm f/4-5.6 IS USM");
-        _lensTypeById.put(198, "Canon EF 50mm f/1.4 USM or Zeiss Lens");
-        _lensTypeById.put(199, "Canon EF 28-80mm f/3.5-5.6 USM");
-        _lensTypeById.put(200, "Canon EF 75-300mm f/4-5.6 USM");
-        _lensTypeById.put(201, "Canon EF 28-80mm f/3.5-5.6 USM");
-        _lensTypeById.put(202, "Canon EF 28-80mm f/3.5-5.6 USM IV");
-        _lensTypeById.put(208, "Canon EF 22-55mm f/4-5.6 USM");
-        _lensTypeById.put(209, "Canon EF 55-200mm f/4.5-5.6");
-        _lensTypeById.put(210, "Canon EF 28-90mm f/4-5.6 USM");
-        _lensTypeById.put(211, "Canon EF 28-200mm f/3.5-5.6 USM");
-        _lensTypeById.put(212, "Canon EF 28-105mm f/4-5.6 USM");
-        _lensTypeById.put(213, "Canon EF 90-300mm f/4.5-5.6 USM or Tamron Lens");
-        _lensTypeById.put(214, "Canon EF-S 18-55mm f/3.5-5.6 USM");
-        _lensTypeById.put(215, "Canon EF 55-200mm f/4.5-5.6 II USM");
-        _lensTypeById.put(217, "Tamron AF 18-270mm f/3.5-6.3 Di II VC PZD");
-        _lensTypeById.put(224, "Canon EF 70-200mm f/2.8L IS");
-        _lensTypeById.put(225, "Canon EF 70-200mm f/2.8L IS + 1.4x");
-        _lensTypeById.put(226, "Canon EF 70-200mm f/2.8L IS + 2x");
-        _lensTypeById.put(227, "Canon EF 70-200mm f/2.8L IS + 2.8x");
-        _lensTypeById.put(228, "Canon EF 28-105mm f/3.5-4.5 USM");
-        _lensTypeById.put(229, "Canon EF 16-35mm f/2.8L");
-        _lensTypeById.put(230, "Canon EF 24-70mm f/2.8L");
-        _lensTypeById.put(231, "Canon EF 17-40mm f/4L");
-        _lensTypeById.put(232, "Canon EF 70-300mm f/4.5-5.6 DO IS USM");
-        _lensTypeById.put(233, "Canon EF 28-300mm f/3.5-5.6L IS");
-        _lensTypeById.put(234, "Canon EF-S 17-85mm f/4-5.6 IS USM or Tokina Lens");
-        _lensTypeById.put(235, "Canon EF-S 10-22mm f/3.5-4.5 USM");
-        _lensTypeById.put(236, "Canon EF-S 60mm f/2.8 Macro USM");
-        _lensTypeById.put(237, "Canon EF 24-105mm f/4L IS");
-        _lensTypeById.put(238, "Canon EF 70-300mm f/4-5.6 IS USM");
-        _lensTypeById.put(239, "Canon EF 85mm f/1.2L II");
-        _lensTypeById.put(240, "Canon EF-S 17-55mm f/2.8 IS USM");
-        _lensTypeById.put(241, "Canon EF 50mm f/1.2L");
-        _lensTypeById.put(242, "Canon EF 70-200mm f/4L IS");
-        _lensTypeById.put(243, "Canon EF 70-200mm f/4L IS + 1.4x");
-        _lensTypeById.put(244, "Canon EF 70-200mm f/4L IS + 2x");
-        _lensTypeById.put(245, "Canon EF 70-200mm f/4L IS + 2.8x");
-        _lensTypeById.put(246, "Canon EF 16-35mm f/2.8L II");
-        _lensTypeById.put(247, "Canon EF 14mm f/2.8L II USM");
-        _lensTypeById.put(248, "Canon EF 200mm f/2L IS or Sigma Lens");
-        _lensTypeById.put(249, "Canon EF 800mm f/5.6L IS");
-        _lensTypeById.put(250, "Canon EF 24mm f/1.4L II or Sigma Lens");
-        _lensTypeById.put(251, "Canon EF 70-200mm f/2.8L IS II USM");
-        _lensTypeById.put(252, "Canon EF 70-200mm f/2.8L IS II USM + 1.4x");
-        _lensTypeById.put(253, "Canon EF 70-200mm f/2.8L IS II USM + 2x");
-        _lensTypeById.put(254, "Canon EF 100mm f/2.8L Macro IS USM");
-        _lensTypeById.put(255, "Sigma 24-105mm f/4 DG OS HSM | A or Other Sigma Lens");
-        _lensTypeById.put(488, "Canon EF-S 15-85mm f/3.5-5.6 IS USM");
-        _lensTypeById.put(489, "Canon EF 70-300mm f/4-5.6L IS USM");
-        _lensTypeById.put(490, "Canon EF 8-15mm f/4L Fisheye USM");
-        _lensTypeById.put(491, "Canon EF 300mm f/2.8L IS II USM");
-        _lensTypeById.put(492, "Canon EF 400mm f/2.8L IS II USM");
-        _lensTypeById.put(493, "Canon EF 500mm f/4L IS II USM or EF 24-105mm f4L IS USM");
-        _lensTypeById.put(494, "Canon EF 600mm f/4.0L IS II USM");
-        _lensTypeById.put(495, "Canon EF 24-70mm f/2.8L II USM");
-        _lensTypeById.put(496, "Canon EF 200-400mm f/4L IS USM");
-        _lensTypeById.put(499, "Canon EF 200-400mm f/4L IS USM + 1.4x");
-        _lensTypeById.put(502, "Canon EF 28mm f/2.8 IS USM");
-        _lensTypeById.put(503, "Canon EF 24mm f/2.8 IS USM");
-        _lensTypeById.put(504, "Canon EF 24-70mm f/4L IS USM");
-        _lensTypeById.put(505, "Canon EF 35mm f/2 IS USM");
-        _lensTypeById.put(506, "Canon EF 400mm f/4 DO IS II USM");
-        _lensTypeById.put(507, "Canon EF 16-35mm f/4L IS USM");
-        _lensTypeById.put(508, "Canon EF 11-24mm f/4L USM");
-        _lensTypeById.put(747, "Canon EF 100-400mm f/4.5-5.6L IS II USM");
-        _lensTypeById.put(750, "Canon EF 35mm f/1.4L II USM");
-        _lensTypeById.put(4142, "Canon EF-S 18-135mm f/3.5-5.6 IS STM");
-        _lensTypeById.put(4143, "Canon EF-M 18-55mm f/3.5-5.6 IS STM or Tamron Lens");
-        _lensTypeById.put(4144, "Canon EF 40mm f/2.8 STM");
-        _lensTypeById.put(4145, "Canon EF-M 22mm f/2 STM");
-        _lensTypeById.put(4146, "Canon EF-S 18-55mm f/3.5-5.6 IS STM");
-        _lensTypeById.put(4147, "Canon EF-M 11-22mm f/4-5.6 IS STM");
-        _lensTypeById.put(4148, "Canon EF-S 55-250mm f/4-5.6 IS STM");
-        _lensTypeById.put(4149, "Canon EF-M 55-200mm f/4.5-6.3 IS STM");
-        _lensTypeById.put(4150, "Canon EF-S 10-18mm f/4.5-5.6 IS STM");
-        _lensTypeById.put(4152, "Canon EF 24-105mm f/3.5-5.6 IS STM");
-        _lensTypeById.put(4153, "Canon EF-M 15-45mm f/3.5-6.3 IS STM");
-        _lensTypeById.put(4154, "Canon EF-S 24mm f/2.8 STM");
-        _lensTypeById.put(4156, "Canon EF 50mm f/1.8 STM");
-        _lensTypeById.put(36912, "Canon EF-S 18-135mm f/3.5-5.6 IS USM");
-        _lensTypeById.put(65535, "N/A");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/CanonMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,799 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Canon cameras.
- *
- * Thanks to Bill Richards for his contribution to this makernote directory.
- *
- * Many tag definitions explained here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class CanonMakernoteDirectory extends Directory
-{
-    // These TAG_*_ARRAY Exif tags map to arrays of int16 values which are split out into separate 'fake' tags.
-    // When an attempt is made to set one of these on the directory, it is split and the corresponding offset added to the tagType.
-    // So the resulting tag is the offset + the index into the array.
-
-    private static final int TAG_CAMERA_SETTINGS_ARRAY          = 0x0001;
-    private static final int TAG_FOCAL_LENGTH_ARRAY             = 0x0002;
-//    private static final int TAG_FLASH_INFO                     = 0x0003;
-    private static final int TAG_SHOT_INFO_ARRAY                = 0x0004;
-    private static final int TAG_PANORAMA_ARRAY                 = 0x0005;
-
-    public static final int TAG_CANON_IMAGE_TYPE                = 0x0006;
-    public static final int TAG_CANON_FIRMWARE_VERSION          = 0x0007;
-    public static final int TAG_CANON_IMAGE_NUMBER              = 0x0008;
-    public static final int TAG_CANON_OWNER_NAME                = 0x0009;
-    public static final int TAG_CANON_SERIAL_NUMBER             = 0x000C;
-    public static final int TAG_CAMERA_INFO_ARRAY               = 0x000D; // depends upon model, so leave for now
-    public static final int TAG_CANON_FILE_LENGTH               = 0x000E;
-    public static final int TAG_CANON_CUSTOM_FUNCTIONS_ARRAY    = 0x000F; // depends upon model, so leave for now
-    public static final int TAG_MODEL_ID                        = 0x0010;
-    public static final int TAG_MOVIE_INFO_ARRAY                = 0x0011; // not currently decoded as not sure we see it in still images
-    private static final int TAG_AF_INFO_ARRAY                  = 0x0012; // not currently decoded
-    public static final int TAG_THUMBNAIL_IMAGE_VALID_AREA      = 0x0013;
-    public static final int TAG_SERIAL_NUMBER_FORMAT            = 0x0015;
-    public static final int TAG_SUPER_MACRO                     = 0x001A;
-    public static final int TAG_DATE_STAMP_MODE                 = 0x001C;
-    public static final int TAG_MY_COLORS                       = 0x001D;
-    public static final int TAG_FIRMWARE_REVISION               = 0x001E;
-    public static final int TAG_CATEGORIES                      = 0x0023;
-    public static final int TAG_FACE_DETECT_ARRAY_1             = 0x0024;
-    public static final int TAG_FACE_DETECT_ARRAY_2             = 0x0025;
-    public static final int TAG_AF_INFO_ARRAY_2                 = 0x0026;
-    public static final int TAG_IMAGE_UNIQUE_ID                 = 0x0028;
-
-    public static final int TAG_RAW_DATA_OFFSET                 = 0x0081;
-    public static final int TAG_ORIGINAL_DECISION_DATA_OFFSET   = 0x0083;
-
-    public static final int TAG_CUSTOM_FUNCTIONS_1D_ARRAY       = 0x0090; // not currently decoded
-    public static final int TAG_PERSONAL_FUNCTIONS_ARRAY        = 0x0091; // not currently decoded
-    public static final int TAG_PERSONAL_FUNCTION_VALUES_ARRAY  = 0x0092; // not currently decoded
-    public static final int TAG_FILE_INFO_ARRAY                 = 0x0093; // not currently decoded
-    public static final int TAG_AF_POINTS_IN_FOCUS_1D           = 0x0094;
-    public static final int TAG_LENS_MODEL                      = 0x0095;
-    public static final int TAG_SERIAL_INFO_ARRAY               = 0x0096; // not currently decoded
-    public static final int TAG_DUST_REMOVAL_DATA               = 0x0097;
-    public static final int TAG_CROP_INFO                       = 0x0098; // not currently decoded
-    public static final int TAG_CUSTOM_FUNCTIONS_ARRAY_2        = 0x0099; // not currently decoded
-    public static final int TAG_ASPECT_INFO_ARRAY               = 0x009A; // not currently decoded
-    public static final int TAG_PROCESSING_INFO_ARRAY           = 0x00A0; // not currently decoded
-    public static final int TAG_TONE_CURVE_TABLE                = 0x00A1;
-    public static final int TAG_SHARPNESS_TABLE                 = 0x00A2;
-    public static final int TAG_SHARPNESS_FREQ_TABLE            = 0x00A3;
-    public static final int TAG_WHITE_BALANCE_TABLE             = 0x00A4;
-    public static final int TAG_COLOR_BALANCE_ARRAY             = 0x00A9; // not currently decoded
-    public static final int TAG_MEASURED_COLOR_ARRAY            = 0x00AA; // not currently decoded
-    public static final int TAG_COLOR_TEMPERATURE               = 0x00AE;
-    public static final int TAG_CANON_FLAGS_ARRAY               = 0x00B0; // not currently decoded
-    public static final int TAG_MODIFIED_INFO_ARRAY             = 0x00B1; // not currently decoded
-    public static final int TAG_TONE_CURVE_MATCHING             = 0x00B2;
-    public static final int TAG_WHITE_BALANCE_MATCHING          = 0x00B3;
-    public static final int TAG_COLOR_SPACE                     = 0x00B4;
-    public static final int TAG_PREVIEW_IMAGE_INFO_ARRAY        = 0x00B6; // not currently decoded
-    public static final int TAG_VRD_OFFSET                      = 0x00D0;
-    public static final int TAG_SENSOR_INFO_ARRAY               = 0x00E0; // not currently decoded
-
-    public static final int TAG_COLOR_DATA_ARRAY_2              = 0x4001; // depends upon camera model, not currently decoded
-    public static final int TAG_CRW_PARAM                       = 0x4002; // depends upon camera model, not currently decoded
-    public static final int TAG_COLOR_INFO_ARRAY_2              = 0x4003; // not currently decoded
-    public static final int TAG_BLACK_LEVEL                     = 0x4008; // not currently decoded
-    public static final int TAG_CUSTOM_PICTURE_STYLE_FILE_NAME  = 0x4010;
-    public static final int TAG_COLOR_INFO_ARRAY                = 0x4013; // not currently decoded
-    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_1   = 0x4015; // not currently decoded
-    public static final int TAG_VIGNETTING_CORRECTION_ARRAY_2   = 0x4016; // not currently decoded
-    public static final int TAG_LIGHTING_OPTIMIZER_ARRAY        = 0x4018; // not currently decoded
-    public static final int TAG_LENS_INFO_ARRAY                 = 0x4019; // not currently decoded
-    public static final int TAG_AMBIANCE_INFO_ARRAY             = 0x4020; // not currently decoded
-    public static final int TAG_FILTER_INFO_ARRAY               = 0x4024; // not currently decoded
-
-    public final static class CameraSettings
-    {
-        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
-        private static final int OFFSET = 0xC100;
-
-        /**
-         * 1 = Macro
-         * 2 = Normal
-         */
-        public static final int TAG_MACRO_MODE = OFFSET + 0x01;
-        public static final int TAG_SELF_TIMER_DELAY = OFFSET + 0x02;
-        /**
-         * 2 = Normal
-         * 3 = Fine
-         * 5 = Superfine
-         */
-        public static final int TAG_QUALITY = OFFSET + 0x03;
-        /**
-         * 0 = Flash Not Fired
-         * 1 = Auto
-         * 2 = On
-         * 3 = Red Eye Reduction
-         * 4 = Slow Synchro
-         * 5 = Auto + Red Eye Reduction
-         * 6 = On + Red Eye Reduction
-         * 16 = External Flash
-         */
-        public static final int TAG_FLASH_MODE = OFFSET + 0x04;
-        /**
-         * 0 = Single Frame or Timer Mode
-         * 1 = Continuous
-         */
-        public static final int TAG_CONTINUOUS_DRIVE_MODE = OFFSET + 0x05;
-        public static final int TAG_UNKNOWN_2 = OFFSET + 0x06;
-        /**
-         * 0 = One-Shot
-         * 1 = AI Servo
-         * 2 = AI Focus
-         * 3 = Manual Focus
-         * 4 = Single
-         * 5 = Continuous
-         * 6 = Manual Focus
-         */
-        public static final int TAG_FOCUS_MODE_1 = OFFSET + 0x07;
-        public static final int TAG_UNKNOWN_3 = OFFSET + 0x08;
-        public static final int TAG_RECORD_MODE = OFFSET + 0x09;
-        /**
-         * 0 = Large
-         * 1 = Medium
-         * 2 = Small
-         */
-        public static final int TAG_IMAGE_SIZE = OFFSET + 0x0A;
-        /**
-         * 0 = Full Auto
-         * 1 = Manual
-         * 2 = Landscape
-         * 3 = Fast Shutter
-         * 4 = Slow Shutter
-         * 5 = Night
-         * 6 = Black &amp; White
-         * 7 = Sepia
-         * 8 = Portrait
-         * 9 = Sports
-         * 10 = Macro / Close-Up
-         * 11 = Pan Focus
-         */
-        public static final int TAG_EASY_SHOOTING_MODE = OFFSET + 0x0B;
-        /**
-         * 0 = No Digital Zoom
-         * 1 = 2x
-         * 2 = 4x
-         */
-        public static final int TAG_DIGITAL_ZOOM = OFFSET + 0x0C;
-        /**
-         * 0 = Normal
-         * 1 = High
-         * 65535 = Low
-         */
-        public static final int TAG_CONTRAST = OFFSET + 0x0D;
-        /**
-         * 0 = Normal
-         * 1 = High
-         * 65535 = Low
-         */
-        public static final int TAG_SATURATION = OFFSET + 0x0E;
-        /**
-         * 0 = Normal
-         * 1 = High
-         * 65535 = Low
-         */
-        public static final int TAG_SHARPNESS = OFFSET + 0x0F;
-        /**
-         * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed
-         * 15 = Auto ISO
-         * 16 = ISO 50
-         * 17 = ISO 100
-         * 18 = ISO 200
-         * 19 = ISO 400
-         */
-        public static final int TAG_ISO = OFFSET + 0x10;
-        /**
-         * 3 = Evaluative
-         * 4 = Partial
-         * 5 = Centre Weighted
-         */
-        public static final int TAG_METERING_MODE = OFFSET + 0x11;
-        /**
-         * 0 = Manual
-         * 1 = Auto
-         * 3 = Close-up (Macro)
-         * 8 = Locked (Pan Mode)
-         */
-        public static final int TAG_FOCUS_TYPE = OFFSET + 0x12;
-        /**
-         * 12288 = None (Manual Focus)
-         * 12289 = Auto Selected
-         * 12290 = Right
-         * 12291 = Centre
-         * 12292 = Left
-         */
-        public static final int TAG_AF_POINT_SELECTED = OFFSET + 0x13;
-        /**
-         * 0 = Easy Shooting (See Easy Shooting Mode)
-         * 1 = Program
-         * 2 = Tv-Priority
-         * 3 = Av-Priority
-         * 4 = Manual
-         * 5 = A-DEP
-         */
-        public static final int TAG_EXPOSURE_MODE = OFFSET + 0x14;
-        public static final int TAG_UNKNOWN_7 = OFFSET + 0x15;
-        public static final int TAG_LENS_TYPE = OFFSET + 0x16;
-        public static final int TAG_LONG_FOCAL_LENGTH = OFFSET + 0x17;
-        public static final int TAG_SHORT_FOCAL_LENGTH = OFFSET + 0x18;
-        public static final int TAG_FOCAL_UNITS_PER_MM = OFFSET + 0x19;
-        public static final int TAG_MAX_APERTURE = OFFSET + 0x1A;
-        public static final int TAG_MIN_APERTURE = OFFSET + 0x1B;
-        /**
-         * 0 = Flash Did Not Fire
-         * 1 = Flash Fired
-         */
-        public static final int TAG_FLASH_ACTIVITY = OFFSET + 0x1C;
-        public static final int TAG_FLASH_DETAILS = OFFSET + 0x1D;
-        public static final int TAG_FOCUS_CONTINUOUS = OFFSET + 0x1E;
-        public static final int TAG_AE_SETTING = OFFSET + 0x1F;
-        /**
-         * 0 = Focus Mode: Single
-         * 1 = Focus Mode: Continuous
-         */
-        public static final int TAG_FOCUS_MODE_2 = OFFSET + 0x20;
-
-        public static final int TAG_DISPLAY_APERTURE = OFFSET + 0x21;
-        public static final int TAG_ZOOM_SOURCE_WIDTH = OFFSET + 0x22;
-        public static final int TAG_ZOOM_TARGET_WIDTH = OFFSET + 0x23;
-
-        public static final int TAG_SPOT_METERING_MODE = OFFSET + 0x25;
-        public static final int TAG_PHOTO_EFFECT = OFFSET + 0x26;
-        public static final int TAG_MANUAL_FLASH_OUTPUT = OFFSET + 0x27;
-
-        public static final int TAG_COLOR_TONE = OFFSET + 0x29;
-        public static final int TAG_SRAW_QUALITY = OFFSET + 0x2D;
-    }
-
-    public final static class FocalLength
-    {
-        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
-
-        private static final int OFFSET = 0xC200;
-
-        /**
-         * 0 = Auto
-         * 1 = Sunny
-         * 2 = Cloudy
-         * 3 = Tungsten
-         * 4 = Florescent
-         * 5 = Flash
-         * 6 = Custom
-         */
-        public static final int TAG_WHITE_BALANCE = OFFSET + 0x07;
-        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 0x09;
-        public static final int TAG_AF_POINT_USED = OFFSET + 0x0E;
-        /**
-         * The value of this tag may be translated into a flash bias value, in EV.
-         *
-         * 0xffc0 = -2 EV
-         * 0xffcc = -1.67 EV
-         * 0xffd0 = -1.5 EV
-         * 0xffd4 = -1.33 EV
-         * 0xffe0 = -1 EV
-         * 0xffec = -0.67 EV
-         * 0xfff0 = -0.5 EV
-         * 0xfff4 = -0.33 EV
-         * 0x0000 = 0 EV
-         * 0x000c = 0.33 EV
-         * 0x0010 = 0.5 EV
-         * 0x0014 = 0.67 EV
-         * 0x0020 = 1 EV
-         * 0x002c = 1.33 EV
-         * 0x0030 = 1.5 EV
-         * 0x0034 = 1.67 EV
-         * 0x0040 = 2 EV
-         */
-        public static final int TAG_FLASH_BIAS = OFFSET + 0x0F;
-        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 0x10;
-        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 0x11;
-        public static final int TAG_SUBJECT_DISTANCE = OFFSET + 0x13;
-    }
-
-    public final static class ShotInfo
-    {
-        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
-
-        private static final int OFFSET = 0xC400;
-
-        public static final int TAG_AUTO_ISO = OFFSET + 1;
-        public static final int TAG_BASE_ISO = OFFSET + 2;
-        public static final int TAG_MEASURED_EV = OFFSET + 3;
-        public static final int TAG_TARGET_APERTURE = OFFSET + 4;
-        public static final int TAG_TARGET_EXPOSURE_TIME = OFFSET + 5;
-        public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 6;
-        public static final int TAG_WHITE_BALANCE = OFFSET + 7;
-        public static final int TAG_SLOW_SHUTTER = OFFSET + 8;
-        public static final int TAG_SEQUENCE_NUMBER = OFFSET + 9;
-        public static final int TAG_OPTICAL_ZOOM_CODE = OFFSET + 10;
-        public static final int TAG_CAMERA_TEMPERATURE = OFFSET + 12;
-        public static final int TAG_FLASH_GUIDE_NUMBER = OFFSET + 13;
-        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 14;
-        public static final int TAG_FLASH_EXPOSURE_BRACKETING = OFFSET + 15;
-        public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 16;
-        public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 17;
-        public static final int TAG_CONTROL_MODE = OFFSET + 18;
-        public static final int TAG_FOCUS_DISTANCE_UPPER = OFFSET + 19;
-        public static final int TAG_FOCUS_DISTANCE_LOWER = OFFSET + 20;
-        public static final int TAG_F_NUMBER = OFFSET + 21;
-        public static final int TAG_EXPOSURE_TIME = OFFSET + 22;
-        public static final int TAG_MEASURED_EV_2 = OFFSET + 23;
-        public static final int TAG_BULB_DURATION = OFFSET + 24;
-        public static final int TAG_CAMERA_TYPE = OFFSET + 26;
-        public static final int TAG_AUTO_ROTATE = OFFSET + 27;
-        public static final int TAG_ND_FILTER = OFFSET + 28;
-        public static final int TAG_SELF_TIMER_2 = OFFSET + 29;
-        public static final int TAG_FLASH_OUTPUT = OFFSET + 33;
-    }
-
-    public final static class Panorama
-    {
-        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
-
-        private static final int OFFSET = 0xC500;
-
-        public static final int TAG_PANORAMA_FRAME_NUMBER = OFFSET + 2;
-        public static final int TAG_PANORAMA_DIRECTION = OFFSET + 5;
-    }
-
-    public final static class AFInfo
-    {
-        // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
-
-        private static final int OFFSET = 0xD200;
-
-        public static final int TAG_NUM_AF_POINTS = OFFSET;
-        public static final int TAG_VALID_AF_POINTS = OFFSET + 1;
-        public static final int TAG_IMAGE_WIDTH = OFFSET + 2;
-        public static final int TAG_IMAGE_HEIGHT = OFFSET + 3;
-        public static final int TAG_AF_IMAGE_WIDTH = OFFSET + 4;
-        public static final int TAG_AF_IMAGE_HEIGHT = OFFSET + 5;
-        public static final int TAG_AF_AREA_WIDTH = OFFSET + 6;
-        public static final int TAG_AF_AREA_HEIGHT = OFFSET + 7;
-        public static final int TAG_AF_AREA_X_POSITIONS = OFFSET + 8;
-        public static final int TAG_AF_AREA_Y_POSITIONS = OFFSET + 9;
-        public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 10;
-        public static final int TAG_PRIMARY_AF_POINT_1 = OFFSET + 11;
-        public static final int TAG_PRIMARY_AF_POINT_2 = OFFSET + 12; // not sure why there are two of these
-    }
-
-//    /**
-//     * Long Exposure Noise Reduction
-//     * 0 = Off
-//     * 1 = On
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301;
-//
-//    /**
-//     * Shutter/Auto Exposure-lock buttons
-//     * 0 = AF/AE lock
-//     * 1 = AE lock/AF
-//     * 2 = AF/AF lock
-//     * 3 = AE+release/AE+AF
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302;
-//
-//    /**
-//     * Mirror lockup
-//     * 0 = Disable
-//     * 1 = Enable
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303;
-//
-//    /**
-//     * Tv/Av and exposure level
-//     * 0 = 1/2 stop
-//     * 1 = 1/3 stop
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304;
-//
-//    /**
-//     * AF-assist light
-//     * 0 = On (Auto)
-//     * 1 = Off
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305;
-//
-//    /**
-//     * Shutter speed in Av mode
-//     * 0 = Automatic
-//     * 1 = 1/200 (fixed)
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306;
-//
-//    /**
-//     * Auto-Exposure Bracketing sequence/auto cancellation
-//     * 0 = 0,-,+ / Enabled
-//     * 1 = 0,-,+ / Disabled
-//     * 2 = -,0,+ / Enabled
-//     * 3 = -,0,+ / Disabled
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETING = 0xC307;
-//
-//    /**
-//     * Shutter Curtain Sync
-//     * 0 = 1st Curtain Sync
-//     * 1 = 2nd Curtain Sync
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308;
-//
-//    /**
-//     * Lens Auto-Focus stop button Function Switch
-//     * 0 = AF stop
-//     * 1 = Operate AF
-//     * 2 = Lock AE and start timer
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309;
-//
-//    /**
-//     * Auto reduction of fill flash
-//     * 0 = Enable
-//     * 1 = Disable
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A;
-//
-//    /**
-//     * Menu button return position
-//     * 0 = Top
-//     * 1 = Previous (volatile)
-//     * 2 = Previous
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B;
-//
-//    /**
-//     * SET button function when shooting
-//     * 0 = Not Assigned
-//     * 1 = Change Quality
-//     * 2 = Change ISO Speed
-//     * 3 = Select Parameters
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;
-//
-//    /**
-//     * Sensor cleaning
-//     * 0 = Disable
-//     * 1 = Enable
-//     */
-//    public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_CANON_FIRMWARE_VERSION, "Firmware Version");
-        _tagNameMap.put(TAG_CANON_IMAGE_NUMBER, "Image Number");
-        _tagNameMap.put(TAG_CANON_IMAGE_TYPE, "Image Type");
-        _tagNameMap.put(TAG_CANON_OWNER_NAME, "Owner Name");
-        _tagNameMap.put(TAG_CANON_SERIAL_NUMBER, "Camera Serial Number");
-        _tagNameMap.put(TAG_CAMERA_INFO_ARRAY, "Camera Info Array");
-        _tagNameMap.put(TAG_CANON_FILE_LENGTH, "File Length");
-        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTIONS_ARRAY, "Custom Functions");
-        _tagNameMap.put(TAG_MODEL_ID, "Canon Model ID");
-        _tagNameMap.put(TAG_MOVIE_INFO_ARRAY, "Movie Info Array");
-
-        _tagNameMap.put(CameraSettings.TAG_AF_POINT_SELECTED, "AF Point Selected");
-        _tagNameMap.put(CameraSettings.TAG_CONTINUOUS_DRIVE_MODE, "Continuous Drive Mode");
-        _tagNameMap.put(CameraSettings.TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(CameraSettings.TAG_EASY_SHOOTING_MODE, "Easy Shooting Mode");
-        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode");
-        _tagNameMap.put(CameraSettings.TAG_FLASH_DETAILS, "Flash Details");
-        _tagNameMap.put(CameraSettings.TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(CameraSettings.TAG_FOCAL_UNITS_PER_MM, "Focal Units per mm");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_1, "Focus Mode");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_2, "Focus Mode");
-        _tagNameMap.put(CameraSettings.TAG_IMAGE_SIZE, "Image Size");
-        _tagNameMap.put(CameraSettings.TAG_ISO, "Iso");
-        _tagNameMap.put(CameraSettings.TAG_LONG_FOCAL_LENGTH, "Long Focal Length");
-        _tagNameMap.put(CameraSettings.TAG_MACRO_MODE, "Macro Mode");
-        _tagNameMap.put(CameraSettings.TAG_METERING_MODE, "Metering Mode");
-        _tagNameMap.put(CameraSettings.TAG_SATURATION, "Saturation");
-        _tagNameMap.put(CameraSettings.TAG_SELF_TIMER_DELAY, "Self Timer Delay");
-        _tagNameMap.put(CameraSettings.TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(CameraSettings.TAG_SHORT_FOCAL_LENGTH, "Short Focal Length");
-        _tagNameMap.put(CameraSettings.TAG_QUALITY, "Quality");
-        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_2, "Unknown Camera Setting 2");
-        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_3, "Unknown Camera Setting 3");
-        _tagNameMap.put(CameraSettings.TAG_RECORD_MODE, "Record Mode");
-        _tagNameMap.put(CameraSettings.TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_TYPE, "Focus Type");
-        _tagNameMap.put(CameraSettings.TAG_UNKNOWN_7, "Unknown Camera Setting 7");
-        _tagNameMap.put(CameraSettings.TAG_LENS_TYPE, "Lens Type");
-        _tagNameMap.put(CameraSettings.TAG_MAX_APERTURE, "Max Aperture");
-        _tagNameMap.put(CameraSettings.TAG_MIN_APERTURE, "Min Aperture");
-        _tagNameMap.put(CameraSettings.TAG_FLASH_ACTIVITY, "Flash Activity");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_CONTINUOUS, "Focus Continuous");
-        _tagNameMap.put(CameraSettings.TAG_AE_SETTING, "AE Setting");
-        _tagNameMap.put(CameraSettings.TAG_DISPLAY_APERTURE, "Display Aperture");
-        _tagNameMap.put(CameraSettings.TAG_ZOOM_SOURCE_WIDTH, "Zoom Source Width");
-        _tagNameMap.put(CameraSettings.TAG_ZOOM_TARGET_WIDTH, "Zoom Target Width");
-        _tagNameMap.put(CameraSettings.TAG_SPOT_METERING_MODE, "Spot Metering Mode");
-        _tagNameMap.put(CameraSettings.TAG_PHOTO_EFFECT, "Photo Effect");
-        _tagNameMap.put(CameraSettings.TAG_MANUAL_FLASH_OUTPUT, "Manual Flash Output");
-        _tagNameMap.put(CameraSettings.TAG_COLOR_TONE, "Color Tone");
-        _tagNameMap.put(CameraSettings.TAG_SRAW_QUALITY, "SRAW Quality");
-
-        _tagNameMap.put(FocalLength.TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(FocalLength.TAG_SEQUENCE_NUMBER, "Sequence Number");
-        _tagNameMap.put(FocalLength.TAG_AF_POINT_USED, "AF Point Used");
-        _tagNameMap.put(FocalLength.TAG_FLASH_BIAS, "Flash Bias");
-        _tagNameMap.put(FocalLength.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
-        _tagNameMap.put(FocalLength.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
-        _tagNameMap.put(FocalLength.TAG_SUBJECT_DISTANCE, "Subject Distance");
-
-        _tagNameMap.put(ShotInfo.TAG_AUTO_ISO, "Auto ISO");
-        _tagNameMap.put(ShotInfo.TAG_BASE_ISO, "Base ISO");
-        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV, "Measured EV");
-        _tagNameMap.put(ShotInfo.TAG_TARGET_APERTURE, "Target Aperture");
-        _tagNameMap.put(ShotInfo.TAG_TARGET_EXPOSURE_TIME, "Target Exposure Time");
-        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_COMPENSATION, "Exposure Compensation");
-        _tagNameMap.put(ShotInfo.TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(ShotInfo.TAG_SLOW_SHUTTER, "Slow Shutter");
-        _tagNameMap.put(ShotInfo.TAG_SEQUENCE_NUMBER, "Sequence Number");
-        _tagNameMap.put(ShotInfo.TAG_OPTICAL_ZOOM_CODE, "Optical Zoom Code");
-        _tagNameMap.put(ShotInfo.TAG_CAMERA_TEMPERATURE, "Camera Temperature");
-        _tagNameMap.put(ShotInfo.TAG_FLASH_GUIDE_NUMBER, "Flash Guide Number");
-        _tagNameMap.put(ShotInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus");
-        _tagNameMap.put(ShotInfo.TAG_FLASH_EXPOSURE_BRACKETING, "Flash Exposure Compensation");
-        _tagNameMap.put(ShotInfo.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing");
-        _tagNameMap.put(ShotInfo.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value");
-        _tagNameMap.put(ShotInfo.TAG_CONTROL_MODE, "Control Mode");
-        _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_UPPER, "Focus Distance Upper");
-        _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_LOWER, "Focus Distance Lower");
-        _tagNameMap.put(ShotInfo.TAG_F_NUMBER, "F Number");
-        _tagNameMap.put(ShotInfo.TAG_EXPOSURE_TIME, "Exposure Time");
-        _tagNameMap.put(ShotInfo.TAG_MEASURED_EV_2, "Measured EV 2");
-        _tagNameMap.put(ShotInfo.TAG_BULB_DURATION, "Bulb Duration");
-        _tagNameMap.put(ShotInfo.TAG_CAMERA_TYPE, "Camera Type");
-        _tagNameMap.put(ShotInfo.TAG_AUTO_ROTATE, "Auto Rotate");
-        _tagNameMap.put(ShotInfo.TAG_ND_FILTER, "ND Filter");
-        _tagNameMap.put(ShotInfo.TAG_SELF_TIMER_2, "Self Timer 2");
-        _tagNameMap.put(ShotInfo.TAG_FLASH_OUTPUT, "Flash Output");
-
-        _tagNameMap.put(Panorama.TAG_PANORAMA_FRAME_NUMBER, "Panorama Frame Number");
-        _tagNameMap.put(Panorama.TAG_PANORAMA_DIRECTION, "Panorama Direction");
-
-        _tagNameMap.put(AFInfo.TAG_NUM_AF_POINTS, "AF Point Count");
-        _tagNameMap.put(AFInfo.TAG_VALID_AF_POINTS, "Valid AF Point Count");
-        _tagNameMap.put(AFInfo.TAG_IMAGE_WIDTH, "Image Width");
-        _tagNameMap.put(AFInfo.TAG_IMAGE_HEIGHT, "Image Height");
-        _tagNameMap.put(AFInfo.TAG_AF_IMAGE_WIDTH, "AF Image Width");
-        _tagNameMap.put(AFInfo.TAG_AF_IMAGE_HEIGHT, "AF Image Height");
-        _tagNameMap.put(AFInfo.TAG_AF_AREA_WIDTH, "AF Area Width");
-        _tagNameMap.put(AFInfo.TAG_AF_AREA_HEIGHT, "AF Area Height");
-        _tagNameMap.put(AFInfo.TAG_AF_AREA_X_POSITIONS, "AF Area X Positions");
-        _tagNameMap.put(AFInfo.TAG_AF_AREA_Y_POSITIONS, "AF Area Y Positions");
-        _tagNameMap.put(AFInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus");
-        _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_1, "Primary AF Point 1");
-        _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_2, "Primary AF Point 2");
-
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS, "Shutter/Auto Exposure-lock Buttons");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP, "Mirror Lockup");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL, "Tv/Av And Exposure Level");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT, "AF-Assist Light");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE, "Shutter Speed in Av Mode");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_BRACKETING, "Auto-Exposure Bracketing Sequence/Auto Cancellation");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC, "Shutter Curtain Sync");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_STOP, "Lens Auto-Focus Stop Button Function Switch");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION, "Auto Reduction of Fill Flash");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN, "Menu Button Return Position");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION, "SET Button Function When Shooting");
-//        _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING, "Sensor Cleaning");
-
-        _tagNameMap.put(TAG_THUMBNAIL_IMAGE_VALID_AREA, "Thumbnail Image Valid Area");
-        _tagNameMap.put(TAG_SERIAL_NUMBER_FORMAT, "Serial Number Format");
-        _tagNameMap.put(TAG_SUPER_MACRO, "Super Macro");
-        _tagNameMap.put(TAG_DATE_STAMP_MODE, "Date Stamp Mode");
-        _tagNameMap.put(TAG_MY_COLORS, "My Colors");
-        _tagNameMap.put(TAG_FIRMWARE_REVISION, "Firmware Revision");
-        _tagNameMap.put(TAG_CATEGORIES, "Categories");
-        _tagNameMap.put(TAG_FACE_DETECT_ARRAY_1, "Face Detect Array 1");
-        _tagNameMap.put(TAG_FACE_DETECT_ARRAY_2, "Face Detect Array 2");
-        _tagNameMap.put(TAG_AF_INFO_ARRAY_2, "AF Info Array 2");
-        _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Image Unique ID");
-        _tagNameMap.put(TAG_RAW_DATA_OFFSET, "Raw Data Offset");
-        _tagNameMap.put(TAG_ORIGINAL_DECISION_DATA_OFFSET, "Original Decision Data Offset");
-        _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_1D_ARRAY, "Custom Functions (1D) Array");
-        _tagNameMap.put(TAG_PERSONAL_FUNCTIONS_ARRAY, "Personal Functions Array");
-        _tagNameMap.put(TAG_PERSONAL_FUNCTION_VALUES_ARRAY, "Personal Function Values Array");
-        _tagNameMap.put(TAG_FILE_INFO_ARRAY, "File Info Array");
-        _tagNameMap.put(TAG_AF_POINTS_IN_FOCUS_1D, "AF Points in Focus (1D)");
-        _tagNameMap.put(TAG_LENS_MODEL, "Lens Model");
-        _tagNameMap.put(TAG_SERIAL_INFO_ARRAY, "Serial Info Array");
-        _tagNameMap.put(TAG_DUST_REMOVAL_DATA, "Dust Removal Data");
-        _tagNameMap.put(TAG_CROP_INFO, "Crop Info");
-        _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_ARRAY_2, "Custom Functions Array 2");
-        _tagNameMap.put(TAG_ASPECT_INFO_ARRAY, "Aspect Information Array");
-        _tagNameMap.put(TAG_PROCESSING_INFO_ARRAY, "Processing Information Array");
-        _tagNameMap.put(TAG_TONE_CURVE_TABLE, "Tone Curve Table");
-        _tagNameMap.put(TAG_SHARPNESS_TABLE, "Sharpness Table");
-        _tagNameMap.put(TAG_SHARPNESS_FREQ_TABLE, "Sharpness Frequency Table");
-        _tagNameMap.put(TAG_WHITE_BALANCE_TABLE, "White Balance Table");
-        _tagNameMap.put(TAG_COLOR_BALANCE_ARRAY, "Color Balance Array");
-        _tagNameMap.put(TAG_MEASURED_COLOR_ARRAY, "Measured Color Array");
-        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
-        _tagNameMap.put(TAG_CANON_FLAGS_ARRAY, "Canon Flags Array");
-        _tagNameMap.put(TAG_MODIFIED_INFO_ARRAY, "Modified Information Array");
-        _tagNameMap.put(TAG_TONE_CURVE_MATCHING, "Tone Curve Matching");
-        _tagNameMap.put(TAG_WHITE_BALANCE_MATCHING, "White Balance Matching");
-        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
-        _tagNameMap.put(TAG_PREVIEW_IMAGE_INFO_ARRAY, "Preview Image Info Array");
-        _tagNameMap.put(TAG_VRD_OFFSET, "VRD Offset");
-        _tagNameMap.put(TAG_SENSOR_INFO_ARRAY, "Sensor Information Array");
-        _tagNameMap.put(TAG_COLOR_DATA_ARRAY_2, "Color Data Array 1");
-        _tagNameMap.put(TAG_CRW_PARAM, "CRW Parameters");
-        _tagNameMap.put(TAG_COLOR_INFO_ARRAY_2, "Color Data Array 2");
-        _tagNameMap.put(TAG_BLACK_LEVEL, "Black Level");
-        _tagNameMap.put(TAG_CUSTOM_PICTURE_STYLE_FILE_NAME, "Custom Picture Style File Name");
-        _tagNameMap.put(TAG_COLOR_INFO_ARRAY, "Color Info Array");
-        _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_1, "Vignetting Correction Array 1");
-        _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_2, "Vignetting Correction Array 2");
-        _tagNameMap.put(TAG_LIGHTING_OPTIMIZER_ARRAY, "Lighting Optimizer Array");
-        _tagNameMap.put(TAG_LENS_INFO_ARRAY, "Lens Info Array");
-        _tagNameMap.put(TAG_AMBIANCE_INFO_ARRAY, "Ambiance Info Array");
-        _tagNameMap.put(TAG_FILTER_INFO_ARRAY, "Filter Info Array");
-    }
-
-    public CanonMakernoteDirectory()
-    {
-        this.setDescriptor(new CanonMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Canon Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    @Override
-    public void setObjectArray(int tagType, @NotNull Object array)
-    {
-        // TODO is there some way to drop out 'null' or 'zero' values that are present in the array to reduce the noise?
-
-        if (!(array instanceof int[])) {
-            // no special handling...
-            super.setObjectArray(tagType, array);
-            return;
-        }
-
-        // Certain Canon tags contain arrays of values that we split into 'fake' tags as each
-        // index in the array has its own meaning and decoding.
-        // Pick those tags out here and throw away the original array.
-        // Otherwise just add as usual.
-        switch (tagType) {
-            case TAG_CAMERA_SETTINGS_ARRAY: {
-                int[] ints = (int[])array;
-                for (int i = 0; i < ints.length; i++)
-                    setInt(CameraSettings.OFFSET + i, ints[i]);
-                break;
-            }
-            case TAG_FOCAL_LENGTH_ARRAY: {
-                int[] ints = (int[])array;
-                for (int i = 0; i < ints.length; i++)
-                    setInt(FocalLength.OFFSET + i, ints[i]);
-                break;
-            }
-            case TAG_SHOT_INFO_ARRAY: {
-                int[] ints = (int[])array;
-                for (int i = 0; i < ints.length; i++)
-                    setInt(ShotInfo.OFFSET + i, ints[i]);
-                break;
-            }
-            case TAG_PANORAMA_ARRAY: {
-                int[] ints = (int[])array;
-                for (int i = 0; i < ints.length; i++)
-                    setInt(Panorama.OFFSET + i, ints[i]);
-                break;
-            }
-            // TODO the interpretation of the custom functions tag depends upon the camera model
-//            case TAG_CANON_CUSTOM_FUNCTIONS_ARRAY:
-//                int subTagTypeBase = 0xC300;
-//                // we intentionally skip the first array member
-//                for (int i = 1; i < ints.length; i++)
-//                    setInt(subTagTypeBase + i + 1, ints[i] & 0x0F);
-//                break;
-            case TAG_AF_INFO_ARRAY: {
-                // Notes from Exiftool 10.10 by Phil Harvey, lib\Image\Exiftool\Canon.pm:
-                // Auto-focus information used by many older Canon models. The values in this
-                // record are sequential, and some have variable sizes based on the value of
-                // numafpoints (which may be 1,5,7,9,15,45, or 53). The AFArea coordinates are
-                // given in a system where the image has dimensions given by AFImageWidth and
-                // AFImageHeight, and 0,0 is the image center. The direction of the Y axis
-                // depends on the camera model, with positive Y upwards for EOS models, but
-                // apparently downwards for PowerShot models.
-
-                // AFInfo is another array with 'fake' tags. The first int of the array contains
-                // the number of AF points. Iterate through the array one byte at a time, generally
-                // assuming one byte corresponds to one tag UNLESS certain tag numbers are encountered.
-                // For these, read specific subsequent bytes from the array based on the tag type. The
-                // number of bytes read can vary.
-
-                int[] values = (int[])array;
-                int numafpoints = values[0];
-                int tagnumber = 0;
-                for (int i = 0; i < values.length; i++)
-                {
-                    // These two tags store 'numafpoints' bytes of data in the array
-                    if (AFInfo.OFFSET + tagnumber == AFInfo.TAG_AF_AREA_X_POSITIONS ||
-                        AFInfo.OFFSET + tagnumber == AFInfo.TAG_AF_AREA_Y_POSITIONS)
-                    {
-                        // There could be incorrect data in the array, so boundary check
-                        if (values.length - 1 >= (i + numafpoints))
-                        {
-                            short[] areaPositions = new short[numafpoints];
-                            for (int j = 0; j < areaPositions.length; j++)
-                                areaPositions[j] = (short)values[i + j];
-
-                            super.setObjectArray(AFInfo.OFFSET + tagnumber, areaPositions);
-                        }
-                        i += numafpoints - 1;   // assume these bytes are processed and skip
-                    }
-                    else if (AFInfo.OFFSET + tagnumber == AFInfo.TAG_AF_POINTS_IN_FOCUS)
-                    {
-                        short[] pointsInFocus = new short[((numafpoints + 15) / 16)];
-
-                        // There could be incorrect data in the array, so boundary check
-                        if (values.length - 1 >= (i + pointsInFocus.length))
-                        {
-                            for (int j = 0; j < pointsInFocus.length; j++)
-                                pointsInFocus[j] = (short)values[i + j];
-
-                            super.setObjectArray(AFInfo.OFFSET + tagnumber, pointsInFocus);
-                        }
-                        i += pointsInFocus.length - 1;  // assume these bytes are processed and skip
-                    }
-                    else
-                        super.setObjectArray(AFInfo.OFFSET + tagnumber, values[i]);
-                    tagnumber++;
-                }
-                break;
-            }
-            default: {
-                // no special handling...
-                super.setObjectArray(tagType, array);
-                break;
-            }
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,201 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.CasioType1MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link CasioType1MakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class CasioType1MakernoteDescriptor extends TagDescriptor<CasioType1MakernoteDirectory>
-{
-    public CasioType1MakernoteDescriptor(@NotNull CasioType1MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_RECORDING_MODE:
-                return getRecordingModeDescription();
-            case TAG_QUALITY:
-                return getQualityDescription();
-            case TAG_FOCUSING_MODE:
-                return getFocusingModeDescription();
-            case TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            case TAG_FLASH_INTENSITY:
-                return getFlashIntensityDescription();
-            case TAG_OBJECT_DISTANCE:
-                return getObjectDistanceDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_SATURATION:
-                return getSaturationDescription();
-            case TAG_CCD_SENSITIVITY:
-                return getCcdSensitivityDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getCcdSensitivityDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CCD_SENSITIVITY);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            // these four for QV3000
-            case 64: return "Normal";
-            case 125: return "+1.0";
-            case 250: return "+2.0";
-            case 244: return "+3.0";
-            // these two for QV8000/2000
-            case 80: return "Normal (ISO 80 equivalent)";
-            case 100: return "High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSaturationDescription()
-    {
-        return getIndexedDescription(TAG_SATURATION, "Normal", "Low", "High");
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        return getIndexedDescription(TAG_CONTRAST, "Normal", "Low", "High");
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        return getIndexedDescription(TAG_SHARPNESS, "Normal", "Soft", "Hard");
-    }
-
-    @Nullable
-    public String getDigitalZoomDescription()
-    {
-        Integer value = _directory.getInteger(TAG_DIGITAL_ZOOM);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0x10000: return "No digital zoom";
-            case 0x10001: return "2x digital zoom";
-            case 0x20000: return "2x digital zoom";
-            case 0x40000: return "4x digital zoom";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        Integer value = _directory.getInteger(TAG_WHITE_BALANCE);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 1: return "Auto";
-            case 2: return "Tungsten";
-            case 3: return "Daylight";
-            case 4: return "Florescent";
-            case 5: return "Shade";
-            case 129: return "Manual";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getObjectDistanceDescription()
-    {
-        Integer value = _directory.getInteger(TAG_OBJECT_DISTANCE);
-        return value == null ? null : getFocalLengthDescription(value);
-    }
-
-    @Nullable
-    public String getFlashIntensityDescription()
-    {
-        Integer value = _directory.getInteger(TAG_FLASH_INTENSITY);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 11: return "Weak";
-            case 13: return "Normal";
-            case 15: return "Strong";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_MODE, 1, "Auto", "On", "Off", "Red eye reduction");
-    }
-
-    @Nullable
-    public String getFocusingModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUSING_MODE, 2, "Macro", "Auto focus", "Manual focus", "Infinity");
-    }
-
-    @Nullable
-    public String getQualityDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY, 1, "Economy", "Normal", "Fine");
-    }
-
-    @Nullable
-    public String getRecordingModeDescription()
-    {
-        return getIndexedDescription(TAG_RECORDING_MODE, 1, "Single shutter", "Panorama", "Night scene", "Portrait", "Landscape");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/CasioType1MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,105 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Casio (type 1) cameras.
- *
- * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment.
- * Makernote data begins immediately (no header).
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class CasioType1MakernoteDirectory extends Directory
-{
-    public static final int TAG_RECORDING_MODE = 0x0001;
-    public static final int TAG_QUALITY = 0x0002;
-    public static final int TAG_FOCUSING_MODE = 0x0003;
-    public static final int TAG_FLASH_MODE = 0x0004;
-    public static final int TAG_FLASH_INTENSITY = 0x0005;
-    public static final int TAG_OBJECT_DISTANCE = 0x0006;
-    public static final int TAG_WHITE_BALANCE = 0x0007;
-    public static final int TAG_UNKNOWN_1 = 0x0008;
-    public static final int TAG_UNKNOWN_2 = 0x0009;
-    public static final int TAG_DIGITAL_ZOOM = 0x000A;
-    public static final int TAG_SHARPNESS = 0x000B;
-    public static final int TAG_CONTRAST = 0x000C;
-    public static final int TAG_SATURATION = 0x000D;
-    public static final int TAG_UNKNOWN_3 = 0x000E;
-    public static final int TAG_UNKNOWN_4 = 0x000F;
-    public static final int TAG_UNKNOWN_5 = 0x0010;
-    public static final int TAG_UNKNOWN_6 = 0x0011;
-    public static final int TAG_UNKNOWN_7 = 0x0012;
-    public static final int TAG_UNKNOWN_8 = 0x0013;
-    public static final int TAG_CCD_SENSITIVITY = 0x0014;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_CCD_SENSITIVITY, "CCD Sensitivity");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_FLASH_INTENSITY, "Flash Intensity");
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(TAG_FOCUSING_MODE, "Focusing Mode");
-        _tagNameMap.put(TAG_OBJECT_DISTANCE, "Object Distance");
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_RECORDING_MODE, "Recording Mode");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_UNKNOWN_1, "Makernote Unknown 1");
-        _tagNameMap.put(TAG_UNKNOWN_2, "Makernote Unknown 2");
-        _tagNameMap.put(TAG_UNKNOWN_3, "Makernote Unknown 3");
-        _tagNameMap.put(TAG_UNKNOWN_4, "Makernote Unknown 4");
-        _tagNameMap.put(TAG_UNKNOWN_5, "Makernote Unknown 5");
-        _tagNameMap.put(TAG_UNKNOWN_6, "Makernote Unknown 6");
-        _tagNameMap.put(TAG_UNKNOWN_7, "Makernote Unknown 7");
-        _tagNameMap.put(TAG_UNKNOWN_8, "Makernote Unknown 8");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-    }
-
-    public CasioType1MakernoteDirectory()
-    {
-        this.setDescriptor(new CasioType1MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Casio Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,320 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.CasioType2MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link CasioType2MakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class CasioType2MakernoteDescriptor extends TagDescriptor<CasioType2MakernoteDirectory>
-{
-    public CasioType2MakernoteDescriptor(@NotNull CasioType2MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_THUMBNAIL_DIMENSIONS:
-                return getThumbnailDimensionsDescription();
-            case TAG_THUMBNAIL_SIZE:
-                return getThumbnailSizeDescription();
-            case TAG_THUMBNAIL_OFFSET:
-                return getThumbnailOffsetDescription();
-            case TAG_QUALITY_MODE:
-                return getQualityModeDescription();
-            case TAG_IMAGE_SIZE:
-                return getImageSizeDescription();
-            case TAG_FOCUS_MODE_1:
-                return getFocusMode1Description();
-            case TAG_ISO_SENSITIVITY:
-                return getIsoSensitivityDescription();
-            case TAG_WHITE_BALANCE_1:
-                return getWhiteBalance1Description();
-            case TAG_FOCAL_LENGTH:
-                return getFocalLengthDescription();
-            case TAG_SATURATION:
-                return getSaturationDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case TAG_PREVIEW_THUMBNAIL:
-                return getCasioPreviewThumbnailDescription();
-            case TAG_WHITE_BALANCE_BIAS:
-                return getWhiteBalanceBiasDescription();
-            case TAG_WHITE_BALANCE_2:
-                return getWhiteBalance2Description();
-            case TAG_OBJECT_DISTANCE:
-                return getObjectDistanceDescription();
-            case TAG_FLASH_DISTANCE:
-                return getFlashDistanceDescription();
-            case TAG_RECORD_MODE:
-                return getRecordModeDescription();
-            case TAG_SELF_TIMER:
-                return getSelfTimerDescription();
-            case TAG_QUALITY:
-                return getQualityDescription();
-            case TAG_FOCUS_MODE_2:
-                return getFocusMode2Description();
-            case TAG_TIME_ZONE:
-                return getTimeZoneDescription();
-            case TAG_CCD_ISO_SENSITIVITY:
-                return getCcdIsoSensitivityDescription();
-            case TAG_COLOUR_MODE:
-                return getColourModeDescription();
-            case TAG_ENHANCEMENT:
-                return getEnhancementDescription();
-            case TAG_FILTER:
-                return getFilterDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getFilterDescription()
-    {
-        return getIndexedDescription(TAG_FILTER, "Off");
-    }
-
-    @Nullable
-    public String getEnhancementDescription()
-    {
-        return getIndexedDescription(TAG_ENHANCEMENT, "Off");
-    }
-
-    @Nullable
-    public String getColourModeDescription()
-    {
-        return getIndexedDescription(TAG_COLOUR_MODE, "Off");
-    }
-
-    @Nullable
-    public String getCcdIsoSensitivityDescription()
-    {
-        return getIndexedDescription(TAG_CCD_ISO_SENSITIVITY, "Off", "On");
-    }
-
-    @Nullable
-    public String getTimeZoneDescription()
-    {
-        return _directory.getString(TAG_TIME_ZONE);
-    }
-
-    @Nullable
-    public String getFocusMode2Description()
-    {
-        Integer value = _directory.getInteger(TAG_FOCUS_MODE_2);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 1: return "Fixation";
-            case 6: return "Multi-Area Focus";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getQualityDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY, 3, "Fine");
-    }
-
-    @Nullable
-    public String getSelfTimerDescription()
-    {
-        return getIndexedDescription(TAG_SELF_TIMER, 1, "Off");
-    }
-
-    @Nullable
-    public String getRecordModeDescription()
-    {
-        return getIndexedDescription(TAG_RECORD_MODE, 2, "Normal");
-    }
-
-    @Nullable
-    public String getFlashDistanceDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_DISTANCE, "Off");
-    }
-
-    @Nullable
-    public String getObjectDistanceDescription()
-    {
-        Integer value = _directory.getInteger(TAG_OBJECT_DISTANCE);
-        if (value == null)
-            return null;
-        return Integer.toString(value) + " mm";
-    }
-
-    @Nullable
-    public String getWhiteBalance2Description()
-    {
-        Integer value = _directory.getInteger(TAG_WHITE_BALANCE_2);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Manual";
-            case 1: return "Auto"; // unsure about this
-            case 4: return "Flash"; // unsure about this
-            case 12: return "Flash";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceBiasDescription()
-    {
-        return _directory.getString(TAG_WHITE_BALANCE_BIAS);
-    }
-
-    @Nullable
-    public String getCasioPreviewThumbnailDescription()
-    {
-        final byte[] bytes = _directory.getByteArray(TAG_PREVIEW_THUMBNAIL);
-        if (bytes == null)
-            return null;
-        return "<" + bytes.length + " bytes of image data>";
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        return getIndexedDescription(TAG_SHARPNESS, "-1", "Normal", "+1");
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        return getIndexedDescription(TAG_CONTRAST, "-1", "Normal", "+1");
-    }
-
-    @Nullable
-    public String getSaturationDescription()
-    {
-        return getIndexedDescription(TAG_SATURATION, "-1", "Normal", "+1");
-    }
-
-    @Nullable
-    public String getFocalLengthDescription()
-    {
-        Double value = _directory.getDoubleObject(TAG_FOCAL_LENGTH);
-        return value == null ? null : getFocalLengthDescription(value / 10d);
-    }
-
-    @Nullable
-    public String getWhiteBalance1Description()
-    {
-        return getIndexedDescription(
-            TAG_WHITE_BALANCE_1,
-            "Auto",
-            "Daylight",
-            "Shade",
-            "Tungsten",
-            "Florescent",
-            "Manual"
-        );
-    }
-
-    @Nullable
-    public String getIsoSensitivityDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ISO_SENSITIVITY);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 3: return "50";
-            case 4: return "64";
-            case 6: return "100";
-            case 9: return "200";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFocusMode1Description()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE_1, "Normal", "Macro");
-    }
-
-    @Nullable
-    public String getImageSizeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_IMAGE_SIZE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "640 x 480 pixels";
-            case 4: return "1600 x 1200 pixels";
-            case 5: return "2048 x 1536 pixels";
-            case 20: return "2288 x 1712 pixels";
-            case 21: return "2592 x 1944 pixels";
-            case 22: return "2304 x 1728 pixels";
-            case 36: return "3008 x 2008 pixels";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getQualityModeDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY_MODE, 1, "Fine", "Super Fine");
-    }
-
-    @Nullable
-    public String getThumbnailOffsetDescription()
-    {
-        return _directory.getString(TAG_THUMBNAIL_OFFSET);
-    }
-
-    @Nullable
-    public String getThumbnailSizeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_THUMBNAIL_SIZE);
-        if (value == null)
-            return null;
-        return Integer.toString(value) + " bytes";
-    }
-
-    @Nullable
-    public String getThumbnailDimensionsDescription()
-    {
-        int[] dimensions = _directory.getIntArray(TAG_THUMBNAIL_DIMENSIONS);
-        if (dimensions == null || dimensions.length != 2)
-            return _directory.getString(TAG_THUMBNAIL_DIMENSIONS);
-        return dimensions[0] + " x " + dimensions[1] + " pixels";
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/CasioType2MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,233 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Casio (type 2) cameras.
- *
- * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment.
- * Makernote data begins after a 6-byte header: "QVC\x00\x00\x00"
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class CasioType2MakernoteDirectory extends Directory
-{
-    /**
-     * 2 values - x,y dimensions in pixels.
-     */
-    public static final int TAG_THUMBNAIL_DIMENSIONS = 0x0002;
-    /**
-     * Size in bytes
-     */
-    public static final int TAG_THUMBNAIL_SIZE = 0x0003;
-    /**
-     * Offset of Preview Thumbnail
-     */
-    public static final int TAG_THUMBNAIL_OFFSET = 0x0004;
-    /**
-     * 1 = Fine
-     * 2 = Super Fine
-     */
-    public static final int TAG_QUALITY_MODE = 0x0008;
-    /**
-     * 0 = 640 x 480 pixels
-     * 4 = 1600 x 1200 pixels
-     * 5 = 2048 x 1536 pixels
-     * 20 = 2288 x 1712 pixels
-     * 21 = 2592 x 1944 pixels
-     * 22 = 2304 x 1728 pixels
-     * 36 = 3008 x 2008 pixels
-     */
-    public static final int TAG_IMAGE_SIZE = 0x0009;
-    /**
-     * 0 = Normal
-     * 1 = Macro
-     */
-    public static final int TAG_FOCUS_MODE_1 = 0x000D;
-    /**
-     * 3 = 50
-     * 4 = 64
-     * 6 = 100
-     * 9 = 200
-     */
-    public static final int TAG_ISO_SENSITIVITY = 0x0014;
-    /**
-     * 0 = Auto
-     * 1 = Daylight
-     * 2 = Shade
-     * 3 = Tungsten
-     * 4 = Fluorescent
-     * 5 = Manual
-     */
-    public static final int TAG_WHITE_BALANCE_1 = 0x0019;
-    /**
-     * Units are tenths of a millimetre
-     */
-    public static final int TAG_FOCAL_LENGTH = 0x001D;
-    /**
-     * 0 = -1
-     * 1 = Normal
-     * 2 = +1
-     */
-    public static final int TAG_SATURATION = 0x001F;
-    /**
-     * 0 = -1
-     * 1 = Normal
-     * 2 = +1
-     */
-    public static final int TAG_CONTRAST = 0x0020;
-    /**
-     * 0 = -1
-     * 1 = Normal
-     * 2 = +1
-     */
-    public static final int TAG_SHARPNESS = 0x0021;
-    /**
-     * See PIM specification here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
-     */
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-    /**
-     * Alternate thumbnail offset
-     */
-    public static final int TAG_PREVIEW_THUMBNAIL = 0x2000;
-    /**
-     *
-     */
-    public static final int TAG_WHITE_BALANCE_BIAS = 0x2011;
-    /**
-     * 12 = Flash
-     * 0 = Manual
-     * 1 = Auto?
-     * 4 = Flash?
-     */
-    public static final int TAG_WHITE_BALANCE_2 = 0x2012;
-    /**
-     * Units are millimetres
-     */
-    public static final int TAG_OBJECT_DISTANCE = 0x2022;
-    /**
-     * 0 = Off
-     */
-    public static final int TAG_FLASH_DISTANCE = 0x2034;
-    /**
-     * 2 = Normal Mode
-     */
-    public static final int TAG_RECORD_MODE = 0x3000;
-    /**
-     * 1 = Off?
-     */
-    public static final int TAG_SELF_TIMER = 0x3001;
-    /**
-     * 3 = Fine
-     */
-    public static final int TAG_QUALITY = 0x3002;
-    /**
-     * 1 = Fixation
-     * 6 = Multi-Area Auto Focus
-     */
-    public static final int TAG_FOCUS_MODE_2 = 0x3003;
-    /**
-     * (string)
-     */
-    public static final int TAG_TIME_ZONE = 0x3006;
-    /**
-     *
-     */
-    public static final int TAG_BESTSHOT_MODE = 0x3007;
-    /**
-     * 0 = Off
-     * 1 = On?
-     */
-    public static final int TAG_CCD_ISO_SENSITIVITY = 0x3014;
-    /**
-     * 0 = Off
-     */
-    public static final int TAG_COLOUR_MODE = 0x3015;
-    /**
-     * 0 = Off
-     */
-    public static final int TAG_ENHANCEMENT = 0x3016;
-    /**
-     * 0 = Off
-     */
-    public static final int TAG_FILTER = 0x3017;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        // TODO add missing names
-        _tagNameMap.put(TAG_THUMBNAIL_DIMENSIONS, "Thumbnail Dimensions");
-        _tagNameMap.put(TAG_THUMBNAIL_SIZE, "Thumbnail Size");
-        _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset");
-        _tagNameMap.put(TAG_QUALITY_MODE, "Quality Mode");
-        _tagNameMap.put(TAG_IMAGE_SIZE, "Image Size");
-        _tagNameMap.put(TAG_FOCUS_MODE_1, "Focus Mode");
-        _tagNameMap.put(TAG_ISO_SENSITIVITY, "ISO Sensitivity");
-        _tagNameMap.put(TAG_WHITE_BALANCE_1, "White Balance");
-        _tagNameMap.put(TAG_FOCAL_LENGTH, "Focal Length");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-        _tagNameMap.put(TAG_PREVIEW_THUMBNAIL, "Casio Preview Thumbnail");
-        _tagNameMap.put(TAG_WHITE_BALANCE_BIAS, "White Balance Bias");
-        _tagNameMap.put(TAG_WHITE_BALANCE_2, "White Balance");
-        _tagNameMap.put(TAG_OBJECT_DISTANCE, "Object Distance");
-        _tagNameMap.put(TAG_FLASH_DISTANCE, "Flash Distance");
-        _tagNameMap.put(TAG_RECORD_MODE, "Record Mode");
-        _tagNameMap.put(TAG_SELF_TIMER, "Self Timer");
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_FOCUS_MODE_2, "Focus Mode");
-        _tagNameMap.put(TAG_TIME_ZONE, "Time Zone");
-        _tagNameMap.put(TAG_BESTSHOT_MODE, "BestShot Mode");
-        _tagNameMap.put(TAG_CCD_ISO_SENSITIVITY, "CCD ISO Sensitivity");
-        _tagNameMap.put(TAG_COLOUR_MODE, "Colour Mode");
-        _tagNameMap.put(TAG_ENHANCEMENT, "Enhancement");
-        _tagNameMap.put(TAG_FILTER, "Filter");
-    }
-
-    public CasioType2MakernoteDirectory()
-    {
-        this.setDescriptor(new CasioType2MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Casio Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,469 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link FujifilmMakernoteDirectory}.
- * <p>
- * Fujifilm added their Makernote tag from the Year 2000's models (e.g.Finepix1400,
- * Finepix4700). It uses IFD format and start from ASCII character 'FUJIFILM', and next 4
- * bytes (value 0x000c) points the offset to first IFD entry.
- * <pre><code>
- * :0000: 46 55 4A 49 46 49 4C 4D-0C 00 00 00 0F 00 00 00 :0000: FUJIFILM........
- * :0010: 07 00 04 00 00 00 30 31-33 30 00 10 02 00 08 00 :0010: ......0130......
- * </code></pre>
- * There are two big differences to the other manufacturers.
- * <ul>
- * <li>Fujifilm's Exif data uses Motorola align, but Makernote ignores it and uses Intel align.</li>
- * <li>
- * The other manufacturer's Makernote counts the "offset to data" from the first byte of TIFF header
- * (same as the other IFD), but Fujifilm counts it from the first byte of Makernote itself.
- * </li>
- * </ul>
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class FujifilmMakernoteDescriptor extends TagDescriptor<FujifilmMakernoteDirectory>
-{
-    public FujifilmMakernoteDescriptor(@NotNull FujifilmMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_MAKERNOTE_VERSION:
-                return getMakernoteVersionDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_COLOR_SATURATION:
-                return getColorSaturationDescription();
-            case TAG_TONE:
-                return getToneDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_NOISE_REDUCTION:
-                return getNoiseReductionDescription();
-            case TAG_HIGH_ISO_NOISE_REDUCTION:
-                return getHighIsoNoiseReductionDescription();
-            case TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            case TAG_FLASH_EV:
-                return getFlashExposureValueDescription();
-            case TAG_MACRO:
-                return getMacroDescription();
-            case TAG_FOCUS_MODE:
-                return getFocusModeDescription();
-            case TAG_SLOW_SYNC:
-                return getSlowSyncDescription();
-            case TAG_PICTURE_MODE:
-                return getPictureModeDescription();
-            case TAG_EXR_AUTO:
-                return getExrAutoDescription();
-            case TAG_EXR_MODE:
-                return getExrModeDescription();
-            case TAG_AUTO_BRACKETING:
-                return getAutoBracketingDescription();
-            case TAG_FINE_PIX_COLOR:
-                return getFinePixColorDescription();
-            case TAG_BLUR_WARNING:
-                return getBlurWarningDescription();
-            case TAG_FOCUS_WARNING:
-                return getFocusWarningDescription();
-            case TAG_AUTO_EXPOSURE_WARNING:
-                return getAutoExposureWarningDescription();
-            case TAG_DYNAMIC_RANGE:
-                return getDynamicRangeDescription();
-            case TAG_FILM_MODE:
-                return getFilmModeDescription();
-            case TAG_DYNAMIC_RANGE_SETTING:
-                return getDynamicRangeSettingDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    private String getMakernoteVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_MAKERNOTE_VERSION, 2);
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_SHARPNESS);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 1: return "Softest";
-            case 2: return "Soft";
-            case 3: return "Normal";
-            case 4: return "Hard";
-            case 5: return "Hardest";
-            case 0x82: return "Medium Soft";
-            case 0x84: return "Medium Hard";
-            case 0x8000: return "Film Simulation";
-            case 0xFFFF: return "N/A";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_WHITE_BALANCE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Auto";
-            case 0x100: return "Daylight";
-            case 0x200: return "Cloudy";
-            case 0x300: return "Daylight Fluorescent";
-            case 0x301: return "Day White Fluorescent";
-            case 0x302: return "White Fluorescent";
-            case 0x303: return "Warm White Fluorescent";
-            case 0x304: return "Living Room Warm White Fluorescent";
-            case 0x400: return "Incandescence";
-            case 0x500: return "Flash";
-            case 0xf00: return "Custom White Balance";
-            case 0xf01: return "Custom White Balance 2";
-            case 0xf02: return "Custom White Balance 3";
-            case 0xf03: return "Custom White Balance 4";
-            case 0xf04: return "Custom White Balance 5";
-            case 0xff0: return "Kelvin";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getColorSaturationDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_COLOR_SATURATION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Normal";
-            case 0x080: return "Medium High";
-            case 0x100: return "High";
-            case 0x180: return "Medium Low";
-            case 0x200: return "Low";
-            case 0x300: return "None (B&W)";
-            case 0x301: return "B&W Green Filter";
-            case 0x302: return "B&W Yellow Filter";
-            case 0x303: return "B&W Blue Filter";
-            case 0x304: return "B&W Sepia";
-            case 0x8000: return "Film Simulation";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getToneDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_TONE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Normal";
-            case 0x080: return "Medium High";
-            case 0x100: return "High";
-            case 0x180: return "Medium Low";
-            case 0x200: return "Low";
-            case 0x300: return "None (B&W)";
-            case 0x8000: return "Film Simulation";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_CONTRAST);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Normal";
-            case 0x100: return "High";
-            case 0x300: return "Low";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getNoiseReductionDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_NOISE_REDUCTION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x040: return "Low";
-            case 0x080: return "Normal";
-            case 0x100: return "N/A";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getHighIsoNoiseReductionDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_HIGH_ISO_NOISE_REDUCTION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Normal";
-            case 0x100: return "Strong";
-            case 0x200: return "Weak";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        return getIndexedDescription(
-            TAG_FLASH_MODE,
-            "Auto",
-            "On",
-            "Off",
-            "Red-eye Reduction",
-            "External"
-        );
-    }
-
-    @Nullable
-    public String getFlashExposureValueDescription()
-    {
-        Rational value = _directory.getRational(TAG_FLASH_EV);
-        return value == null ? null : value.toSimpleString(false) + " EV (Apex)";
-    }
-
-    @Nullable
-    public String getMacroDescription()
-    {
-        return getIndexedDescription(TAG_MACRO, "Off", "On");
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE, "Auto Focus", "Manual Focus");
-    }
-
-    @Nullable
-    public String getSlowSyncDescription()
-    {
-        return getIndexedDescription(TAG_SLOW_SYNC, "Off", "On");
-    }
-
-    @Nullable
-    public String getPictureModeDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_PICTURE_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Auto";
-            case 0x001: return "Portrait scene";
-            case 0x002: return "Landscape scene";
-            case 0x003: return "Macro";
-            case 0x004: return "Sports scene";
-            case 0x005: return "Night scene";
-            case 0x006: return "Program AE";
-            case 0x007: return "Natural Light";
-            case 0x008: return "Anti-blur";
-            case 0x009: return "Beach & Snow";
-            case 0x00a: return "Sunset";
-            case 0x00b: return "Museum";
-            case 0x00c: return "Party";
-            case 0x00d: return "Flower";
-            case 0x00e: return "Text";
-            case 0x00f: return "Natural Light & Flash";
-            case 0x010: return "Beach";
-            case 0x011: return "Snow";
-            case 0x012: return "Fireworks";
-            case 0x013: return "Underwater";
-            case 0x014: return "Portrait with Skin Correction";
-            // skip 0x015
-            case 0x016: return "Panorama";
-            case 0x017: return "Night (Tripod)";
-            case 0x018: return "Pro Low-light";
-            case 0x019: return "Pro Focus";
-            // skip 0x01a
-            case 0x01b: return "Dog Face Detection";
-            case 0x01c: return "Cat Face Detection";
-            case 0x100: return "Aperture priority AE";
-            case 0x200: return "Shutter priority AE";
-            case 0x300: return "Manual exposure";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getExrAutoDescription()
-    {
-        return getIndexedDescription(TAG_EXR_AUTO, "Auto", "Manual");
-    }
-
-    @Nullable
-    public String getExrModeDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_EXR_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x100: return "HR (High Resolution)";
-            case 0x200: return "SN (Signal to Noise Priority)";
-            case 0x300: return "DR (Dynamic Range Priority)";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAutoBracketingDescription()
-    {
-        return getIndexedDescription(
-            TAG_AUTO_BRACKETING,
-            "Off",
-            "On",
-            "No Flash & Flash"
-        );
-    }
-
-    @Nullable
-    public String getFinePixColorDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_FINE_PIX_COLOR);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x00: return "Standard";
-            case 0x10: return "Chrome";
-            case 0x30: return "B&W";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getBlurWarningDescription()
-    {
-        return getIndexedDescription(
-            TAG_BLUR_WARNING,
-            "No Blur Warning",
-            "Blur warning"
-        );
-    }
-
-    @Nullable
-    public String getFocusWarningDescription()
-    {
-        return getIndexedDescription(
-            TAG_FOCUS_WARNING,
-            "Good Focus",
-            "Out Of Focus"
-        );
-    }
-
-    @Nullable
-    public String getAutoExposureWarningDescription()
-    {
-        return getIndexedDescription(
-            TAG_AUTO_EXPOSURE_WARNING,
-            "AE Good",
-            "Over Exposed"
-        );
-    }
-
-    @Nullable
-    public String getDynamicRangeDescription()
-    {
-        return getIndexedDescription(
-            TAG_DYNAMIC_RANGE,
-            1,
-            "Standard",
-            null,
-            "Wide"
-        );
-    }
-
-    @Nullable
-    public String getFilmModeDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_FILM_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "F0/Standard (Provia) ";
-            case 0x100: return "F1/Studio Portrait";
-            case 0x110: return "F1a/Studio Portrait Enhanced Saturation";
-            case 0x120: return "F1b/Studio Portrait Smooth Skin Tone (Astia)";
-            case 0x130: return "F1c/Studio Portrait Increased Sharpness";
-            case 0x200: return "F2/Fujichrome (Velvia)";
-            case 0x300: return "F3/Studio Portrait Ex";
-            case 0x400: return "F4/Velvia";
-            case 0x500: return "Pro Neg. Std";
-            case 0x501: return "Pro Neg. Hi";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getDynamicRangeSettingDescription()
-    {
-        final Integer value = _directory.getInteger(TAG_DYNAMIC_RANGE_SETTING);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x000: return "Auto (100-400%)";
-            case 0x001: return "Manual";
-            case 0x100: return "Standard (100%)";
-            case 0x200: return "Wide 1 (230%)";
-            case 0x201: return "Wide 2 (400%)";
-            case 0x8000: return "Film Simulation";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/FujifilmMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,179 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Fujifilm cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class FujifilmMakernoteDirectory extends Directory
-{
-    public static final int TAG_MAKERNOTE_VERSION = 0x0000;
-    public static final int TAG_SERIAL_NUMBER = 0x0010;
-
-    public static final int TAG_QUALITY = 0x1000;
-    public static final int TAG_SHARPNESS = 0x1001;
-    public static final int TAG_WHITE_BALANCE = 0x1002;
-    public static final int TAG_COLOR_SATURATION = 0x1003;
-    public static final int TAG_TONE = 0x1004;
-    public static final int TAG_COLOR_TEMPERATURE = 0x1005;
-    public static final int TAG_CONTRAST = 0x1006;
-
-    public static final int TAG_WHITE_BALANCE_FINE_TUNE = 0x100a;
-    public static final int TAG_NOISE_REDUCTION = 0x100b;
-    public static final int TAG_HIGH_ISO_NOISE_REDUCTION = 0x100e;
-
-    public static final int TAG_FLASH_MODE = 0x1010;
-    public static final int TAG_FLASH_EV = 0x1011;
-
-    public static final int TAG_MACRO = 0x1020;
-    public static final int TAG_FOCUS_MODE = 0x1021;
-    public static final int TAG_FOCUS_PIXEL = 0x1023;
-
-    public static final int TAG_SLOW_SYNC = 0x1030;
-    public static final int TAG_PICTURE_MODE = 0x1031;
-    public static final int TAG_EXR_AUTO = 0x1033;
-    public static final int TAG_EXR_MODE = 0x1034;
-
-    public static final int TAG_AUTO_BRACKETING = 0x1100;
-    public static final int TAG_SEQUENCE_NUMBER = 0x1101;
-
-    public static final int TAG_FINE_PIX_COLOR = 0x1210;
-
-    public static final int TAG_BLUR_WARNING = 0x1300;
-    public static final int TAG_FOCUS_WARNING = 0x1301;
-    public static final int TAG_AUTO_EXPOSURE_WARNING = 0x1302;
-    public static final int TAG_GE_IMAGE_SIZE = 0x1304;
-
-    public static final int TAG_DYNAMIC_RANGE = 0x1400;
-    public static final int TAG_FILM_MODE = 0x1401;
-    public static final int TAG_DYNAMIC_RANGE_SETTING = 0x1402;
-    public static final int TAG_DEVELOPMENT_DYNAMIC_RANGE = 0x1403;
-    public static final int TAG_MIN_FOCAL_LENGTH = 0x1404;
-    public static final int TAG_MAX_FOCAL_LENGTH = 0x1405;
-    public static final int TAG_MAX_APERTURE_AT_MIN_FOCAL = 0x1406;
-    public static final int TAG_MAX_APERTURE_AT_MAX_FOCAL = 0x1407;
-
-    public static final int TAG_AUTO_DYNAMIC_RANGE = 0x140b;
-
-    public static final int TAG_FACES_DETECTED = 0x4100;
-    /**
-     * Left, top, right and bottom coordinates in full-sized image for each face detected.
-     */
-    public static final int TAG_FACE_POSITIONS = 0x4103;
-    public static final int TAG_FACE_REC_INFO = 0x4282;
-
-    public static final int TAG_FILE_SOURCE = 0x8000;
-    public static final int TAG_ORDER_NUMBER = 0x8002;
-    public static final int TAG_FRAME_NUMBER = 0x8003;
-
-    public static final int TAG_PARALLAX = 0xb211;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
-        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
-
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_COLOR_SATURATION, "Color Saturation");
-        _tagNameMap.put(TAG_TONE, "Tone (Contrast)");
-        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-
-        _tagNameMap.put(TAG_WHITE_BALANCE_FINE_TUNE, "White Balance Fine Tune");
-        _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction");
-        _tagNameMap.put(TAG_HIGH_ISO_NOISE_REDUCTION, "High ISO Noise Reduction");
-
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(TAG_FLASH_EV, "Flash Strength");
-
-        _tagNameMap.put(TAG_MACRO, "Macro");
-        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(TAG_FOCUS_PIXEL, "Focus Pixel");
-
-        _tagNameMap.put(TAG_SLOW_SYNC, "Slow Sync");
-        _tagNameMap.put(TAG_PICTURE_MODE, "Picture Mode");
-        _tagNameMap.put(TAG_EXR_AUTO, "EXR Auto");
-        _tagNameMap.put(TAG_EXR_MODE, "EXR Mode");
-
-        _tagNameMap.put(TAG_AUTO_BRACKETING, "Auto Bracketing");
-        _tagNameMap.put(TAG_SEQUENCE_NUMBER, "Sequence Number");
-
-        _tagNameMap.put(TAG_FINE_PIX_COLOR, "FinePix Color Setting");
-
-        _tagNameMap.put(TAG_BLUR_WARNING, "Blur Warning");
-        _tagNameMap.put(TAG_FOCUS_WARNING, "Focus Warning");
-        _tagNameMap.put(TAG_AUTO_EXPOSURE_WARNING, "AE Warning");
-        _tagNameMap.put(TAG_GE_IMAGE_SIZE, "GE Image Size");
-
-        _tagNameMap.put(TAG_DYNAMIC_RANGE, "Dynamic Range");
-        _tagNameMap.put(TAG_FILM_MODE, "Film Mode");
-        _tagNameMap.put(TAG_DYNAMIC_RANGE_SETTING, "Dynamic Range Setting");
-        _tagNameMap.put(TAG_DEVELOPMENT_DYNAMIC_RANGE, "Development Dynamic Range");
-        _tagNameMap.put(TAG_MIN_FOCAL_LENGTH, "Minimum Focal Length");
-        _tagNameMap.put(TAG_MAX_FOCAL_LENGTH, "Maximum Focal Length");
-        _tagNameMap.put(TAG_MAX_APERTURE_AT_MIN_FOCAL, "Maximum Aperture at Minimum Focal Length");
-        _tagNameMap.put(TAG_MAX_APERTURE_AT_MAX_FOCAL, "Maximum Aperture at Maximum Focal Length");
-
-        _tagNameMap.put(TAG_AUTO_DYNAMIC_RANGE, "Auto Dynamic Range");
-
-        _tagNameMap.put(TAG_FACES_DETECTED, "Faces Detected");
-        _tagNameMap.put(TAG_FACE_POSITIONS, "Face Positions");
-        _tagNameMap.put(TAG_FACE_REC_INFO, "Face Detection Data");
-
-        _tagNameMap.put(TAG_FILE_SOURCE, "File Source");
-        _tagNameMap.put(TAG_ORDER_NUMBER, "Order Number");
-        _tagNameMap.put(TAG_FRAME_NUMBER, "Frame Number");
-
-        _tagNameMap.put(TAG_PARALLAX, "Parallax");
-    }
-
-    public FujifilmMakernoteDirectory()
-    {
-        this.setDescriptor(new FujifilmMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Fujifilm Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,152 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.KodakMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link KodakMakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class KodakMakernoteDescriptor extends TagDescriptor<KodakMakernoteDirectory>
-{
-    public KodakMakernoteDescriptor(@NotNull KodakMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_QUALITY:
-                return getQualityDescription();
-            case TAG_BURST_MODE:
-                return getBurstModeDescription();
-            case TAG_SHUTTER_MODE:
-                return getShutterModeDescription();
-            case TAG_FOCUS_MODE:
-                return getFocusModeDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            case TAG_FLASH_FIRED:
-                return getFlashFiredDescription();
-            case TAG_COLOR_MODE:
-                return getColorModeDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        return getIndexedDescription(TAG_SHARPNESS, "Normal");
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_COLOR_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x001: case 0x2000: return "B&W";
-            case 0x002: case 0x4000: return "Sepia";
-            case 0x003: return "B&W Yellow Filter";
-            case 0x004: return "B&W Red Filter";
-            case 0x020: return "Saturated Color";
-            case 0x040: case 0x200: return "Neutral Color";
-            case 0x100: return "Saturated Color";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFlashFiredDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_FIRED, "No", "Yes");
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_FLASH_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x00: return "Auto";
-            case 0x10: case 0x01: return "Fill Flash";
-            case 0x20: case 0x02: return "Off";
-            case 0x40: case 0x03: return "Red Eye";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(TAG_WHITE_BALANCE, "Auto", "Flash", "Tungsten", "Daylight");
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE, "Normal", null, "Macro");
-    }
-
-    @Nullable
-    public String getShutterModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_SHUTTER_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Auto";
-            case 8: return "Aperture Priority";
-            case 32: return "Manual";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getBurstModeDescription()
-    {
-        return getIndexedDescription(TAG_BURST_MODE, "Off", "On");
-    }
-
-    @Nullable
-    public String getQualityDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY, 1, "Fine", "Normal");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/KodakMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,114 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Kodak cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class KodakMakernoteDirectory extends Directory
-{
-    public final static int TAG_KODAK_MODEL = 0;
-    public final static int TAG_QUALITY = 9;
-    public final static int TAG_BURST_MODE = 10;
-    public final static int TAG_IMAGE_WIDTH = 12;
-    public final static int TAG_IMAGE_HEIGHT = 14;
-    public final static int TAG_YEAR_CREATED = 16;
-    public final static int TAG_MONTH_DAY_CREATED = 18;
-    public final static int TAG_TIME_CREATED = 20;
-    public final static int TAG_BURST_MODE_2 = 24;
-    public final static int TAG_SHUTTER_MODE = 27;
-    public final static int TAG_METERING_MODE = 28;
-    public final static int TAG_SEQUENCE_NUMBER = 29;
-    public final static int TAG_F_NUMBER = 30;
-    public final static int TAG_EXPOSURE_TIME = 32;
-    public final static int TAG_EXPOSURE_COMPENSATION = 36;
-    public final static int TAG_FOCUS_MODE = 56;
-    public final static int TAG_WHITE_BALANCE = 64;
-    public final static int TAG_FLASH_MODE = 92;
-    public final static int TAG_FLASH_FIRED = 93;
-    public final static int TAG_ISO_SETTING = 94;
-    public final static int TAG_ISO = 96;
-    public final static int TAG_TOTAL_ZOOM = 98;
-    public final static int TAG_DATE_TIME_STAMP = 100;
-    public final static int TAG_COLOR_MODE = 102;
-    public final static int TAG_DIGITAL_ZOOM = 104;
-    public final static int TAG_SHARPNESS = 107;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_KODAK_MODEL, "Kodak Model");
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_BURST_MODE, "Burst Mode");
-        _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
-        _tagNameMap.put(TAG_IMAGE_HEIGHT, "Image Height");
-        _tagNameMap.put(TAG_YEAR_CREATED, "Year Created");
-        _tagNameMap.put(TAG_MONTH_DAY_CREATED, "Month/Day Created");
-        _tagNameMap.put(TAG_TIME_CREATED, "Time Created");
-        _tagNameMap.put(TAG_BURST_MODE_2, "Burst Mode 2");
-        _tagNameMap.put(TAG_SHUTTER_MODE, "Shutter Speed");
-        _tagNameMap.put(TAG_METERING_MODE, "Metering Mode");
-        _tagNameMap.put(TAG_SEQUENCE_NUMBER, "Sequence Number");
-        _tagNameMap.put(TAG_F_NUMBER, "F Number");
-        _tagNameMap.put(TAG_EXPOSURE_TIME, "Exposure Time");
-        _tagNameMap.put(TAG_EXPOSURE_COMPENSATION, "Exposure Compensation");
-        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(TAG_FLASH_FIRED, "Flash Fired");
-        _tagNameMap.put(TAG_ISO_SETTING, "ISO Setting");
-        _tagNameMap.put(TAG_ISO, "ISO");
-        _tagNameMap.put(TAG_TOTAL_ZOOM, "Total Zoom");
-        _tagNameMap.put(TAG_DATE_TIME_STAMP, "Date/Time Stamp");
-        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-    }
-
-    public KodakMakernoteDirectory()
-    {
-        this.setDescriptor(new KodakMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Kodak Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,66 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.KyoceraMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link KyoceraMakernoteDirectory}.
- * <p>
- * Some information about this makernote taken from here:
- * http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
- * <p>
- * Most manufacturer's Makernote counts the "offset to data" from the first byte
- * of TIFF header (same as the other IFD), but Kyocera (along with Fujifilm) counts
- * it from the first byte of Makernote itself.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class KyoceraMakernoteDescriptor extends TagDescriptor<KyoceraMakernoteDirectory>
-{
-    public KyoceraMakernoteDescriptor(@NotNull KyoceraMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_PROPRIETARY_THUMBNAIL:
-                return getProprietaryThumbnailDataDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getProprietaryThumbnailDataDescription()
-    {
-        return getByteLengthDescription(TAG_PROPRIETARY_THUMBNAIL);
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/KyoceraMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,66 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Kyocera and Contax cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class KyoceraMakernoteDirectory extends Directory
-{
-    public static final int TAG_PROPRIETARY_THUMBNAIL = 0x0001;
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_PROPRIETARY_THUMBNAIL, "Proprietary Thumbnail Format Data");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-    }
-
-    public KyoceraMakernoteDirectory()
-    {
-        this.setDescriptor(new KyoceraMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Kyocera/Contax Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,128 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.LeicaMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link LeicaMakernoteDirectory}.
- * <p>
- * Tag reference from: http://gvsoft.homedns.org/exif/makernote-leica-type1.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class LeicaMakernoteDescriptor extends TagDescriptor<LeicaMakernoteDirectory>
-{
-    public LeicaMakernoteDescriptor(@NotNull LeicaMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_QUALITY:
-                return getQualityDescription();
-            case TAG_USER_PROFILE:
-                return getUserProfileDescription();
-//            case TAG_SERIAL:
-//                return getSerialNumberDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_EXTERNAL_SENSOR_BRIGHTNESS_VALUE:
-                return getExternalSensorBrightnessValueDescription();
-            case TAG_MEASURED_LV:
-                return getMeasuredLvDescription();
-            case TAG_APPROXIMATE_F_NUMBER:
-                return getApproximateFNumberDescription();
-            case TAG_CAMERA_TEMPERATURE:
-                return getCameraTemperatureDescription();
-            case TAG_WB_RED_LEVEL:
-            case TAG_WB_BLUE_LEVEL:
-            case TAG_WB_GREEN_LEVEL:
-                return getSimpleRational(tagType);
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    private String getCameraTemperatureDescription()
-    {
-        return getFormattedInt(TAG_CAMERA_TEMPERATURE, "%d C");
-    }
-
-    @Nullable
-    private String getApproximateFNumberDescription()
-    {
-        return getSimpleRational(TAG_APPROXIMATE_F_NUMBER);
-    }
-
-    @Nullable
-    private String getMeasuredLvDescription()
-    {
-        return getSimpleRational(TAG_MEASURED_LV);
-    }
-
-    @Nullable
-    private String getExternalSensorBrightnessValueDescription()
-    {
-        return getSimpleRational(TAG_EXTERNAL_SENSOR_BRIGHTNESS_VALUE);
-    }
-
-    @Nullable
-    private String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(TAG_WHITE_BALANCE,
-            "Auto or Manual",
-            "Daylight",
-            "Fluorescent",
-            "Tungsten",
-            "Flash",
-            "Cloudy",
-            "Shadow"
-        );
-    }
-
-    @Nullable
-    private String getUserProfileDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY, 1,
-            "User Profile 1",
-            "User Profile 2",
-            "User Profile 3",
-            "User Profile 0 (Dynamic)"
-        );
-    }
-
-    @Nullable
-    private String getQualityDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY, 1, "Fine", "Basic");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/LeicaMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,108 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to certain Leica cameras.
- * <p>
- * Tag reference from: http://gvsoft.homedns.org/exif/makernote-leica-type1.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class LeicaMakernoteDirectory extends Directory
-{
-    public static final int TAG_QUALITY = 0x0300;
-    public static final int TAG_USER_PROFILE = 0x0302;
-    public static final int TAG_SERIAL_NUMBER = 0x0303;
-    public static final int TAG_WHITE_BALANCE = 0x0304;
-
-    public static final int TAG_LENS_TYPE = 0x0310;
-    public static final int TAG_EXTERNAL_SENSOR_BRIGHTNESS_VALUE = 0x0311;
-    public static final int TAG_MEASURED_LV = 0x0312;
-    public static final int TAG_APPROXIMATE_F_NUMBER = 0x0313;
-
-    public static final int TAG_CAMERA_TEMPERATURE = 0x0320;
-    public static final int TAG_COLOR_TEMPERATURE = 0x0321;
-    public static final int TAG_WB_RED_LEVEL = 0x0322;
-    public static final int TAG_WB_GREEN_LEVEL = 0x0323;
-    public static final int TAG_WB_BLUE_LEVEL = 0x0324;
-
-    public static final int TAG_CCD_VERSION = 0x0330;
-    public static final int TAG_CCD_BOARD_VERSION = 0x0331;
-    public static final int TAG_CONTROLLER_BOARD_VERSION = 0x0332;
-    public static final int TAG_M16_C_VERSION = 0x0333;
-
-    public static final int TAG_IMAGE_ID_NUMBER = 0x0340;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_USER_PROFILE, "User Profile");
-        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-
-        _tagNameMap.put(TAG_LENS_TYPE, "Lens Type");
-        _tagNameMap.put(TAG_EXTERNAL_SENSOR_BRIGHTNESS_VALUE, "External Sensor Brightness Value");
-        _tagNameMap.put(TAG_MEASURED_LV, "Measured LV");
-        _tagNameMap.put(TAG_APPROXIMATE_F_NUMBER, "Approximate F Number");
-
-        _tagNameMap.put(TAG_CAMERA_TEMPERATURE, "Camera Temperature");
-        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
-        _tagNameMap.put(TAG_WB_RED_LEVEL, "WB Red Level");
-        _tagNameMap.put(TAG_WB_GREEN_LEVEL, "WB Green Level");
-        _tagNameMap.put(TAG_WB_BLUE_LEVEL, "WB Blue Level");
-
-        _tagNameMap.put(TAG_CCD_VERSION, "CCD Version");
-        _tagNameMap.put(TAG_CCD_BOARD_VERSION, "CCD Board Version");
-        _tagNameMap.put(TAG_CONTROLLER_BOARD_VERSION, "Controller Board Version");
-        _tagNameMap.put(TAG_M16_C_VERSION, "M16 C Version");
-
-        _tagNameMap.put(TAG_IMAGE_ID_NUMBER, "Image ID Number");
-    }
-
-    public LeicaMakernoteDirectory()
-    {
-        this.setDescriptor(new LeicaMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Leica Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/LeicaType5MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/LeicaType5MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,79 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.LeicaType5MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link LeicaType5MakernoteDirectory}.
- * <p>
- * Tag reference from: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class LeicaType5MakernoteDescriptor extends TagDescriptor<LeicaType5MakernoteDirectory>
-{
-    public LeicaType5MakernoteDescriptor(@NotNull LeicaType5MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagExposureMode:
-                return getExposureModeDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getExposureModeDescription()
-    {
-        byte[] values = _directory.getByteArray(TagExposureMode);
-        if (values == null || values.length < 4)
-            return null;
-
-        String join = String.format("%d %d %d %d", values[0], values[1], values[2], values[3]);
-
-        if(join.equals("0 0 0 0"))
-            return "Program AE";
-        else if(join.equals("1 0 0 0"))
-            return "Aperture-priority AE";
-        else if(join.equals("1 1 0 0"))
-            return "Aperture-priority AE (1)";
-        else if(join.equals("2 0 0 0"))
-            return "Shutter speed priority AE";  // guess
-        else if(join.equals("3 0 0 0"))
-            return "Manual";
-        else
-            return String.format("Unknown (%s)", join);
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/LeicaType5MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/LeicaType5MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,79 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to certain Leica cameras.
- * <p>
- * Tag reference from: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class LeicaType5MakernoteDirectory extends Directory
-{
-    public static final int TagLensModel = 0x0303;
-    public static final int TagOriginalFileName = 0x0407;
-    public static final int TagOriginalDirectory = 0x0408;
-    public static final int TagExposureMode = 0x040d;
-    public static final int TagShotInfo = 0x0410;
-    public static final int TagFilmMode = 0x0412;
-    public static final int TagWbRgbLevels = 0x0413;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagLensModel, "Lens Model");
-        _tagNameMap.put(TagOriginalFileName, "Original File Name");
-        _tagNameMap.put(TagOriginalDirectory, "Original Directory");
-        _tagNameMap.put(TagExposureMode, "Exposure Mode");
-        _tagNameMap.put(TagShotInfo, "Shot Info" );
-        _tagNameMap.put(TagFilmMode, "Film Mode");
-        _tagNameMap.put(TagWbRgbLevels, "WB RGB Levels");
-    }
-
-    public LeicaType5MakernoteDirectory()
-    {
-        this.setDescriptor(new LeicaType5MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Leica Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,170 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.NikonType1MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link NikonType1MakernoteDirectory}.
- * <p>
- * Type-1 is for E-Series cameras prior to (not including) E990.  For example: E700, E800, E900,
- * E900S, E910, E950.
- * <p>
- * Makernote starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from
- * offset 0x08. This is the same as Olympus except start string. Example of actual data
- * structure is shown below.
- * <pre><code>
- * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
- * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
- * </code></pre>
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class NikonType1MakernoteDescriptor extends TagDescriptor<NikonType1MakernoteDirectory>
-{
-    public NikonType1MakernoteDescriptor(@NotNull NikonType1MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_QUALITY:
-                return getQualityDescription();
-            case TAG_COLOR_MODE:
-                return getColorModeDescription();
-            case TAG_IMAGE_ADJUSTMENT:
-                return getImageAdjustmentDescription();
-            case TAG_CCD_SENSITIVITY:
-                return getCcdSensitivityDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_FOCUS:
-                return getFocusDescription();
-            case TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case TAG_CONVERTER:
-                return getConverterDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getConverterDescription()
-    {
-        return getIndexedDescription(TAG_CONVERTER, "None", "Fisheye converter");
-    }
-
-    @Nullable
-    public String getDigitalZoomDescription()
-    {
-        Rational value = _directory.getRational(TAG_DIGITAL_ZOOM);
-        return value == null
-            ? null
-            : value.getNumerator() == 0
-                ? "No digital zoom"
-                : value.toSimpleString(true) + "x digital zoom";
-    }
-
-    @Nullable
-    public String getFocusDescription()
-    {
-        Rational value = _directory.getRational(TAG_FOCUS);
-        return value == null
-            ? null
-            : value.getNumerator() == 1 && value.getDenominator() == 0
-                ? "Infinite"
-                : value.toSimpleString(true);
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(TAG_WHITE_BALANCE,
-            "Auto",
-            "Preset",
-            "Daylight",
-            "Incandescence",
-            "Florescence",
-            "Cloudy",
-            "SpeedLight"
-        );
-    }
-
-    @Nullable
-    public String getCcdSensitivityDescription()
-    {
-        return getIndexedDescription(TAG_CCD_SENSITIVITY,
-            "ISO80",
-            null,
-            "ISO160",
-            null,
-            "ISO320",
-            "ISO100"
-        );
-    }
-
-    @Nullable
-    public String getImageAdjustmentDescription()
-    {
-        return getIndexedDescription(TAG_IMAGE_ADJUSTMENT,
-            "Normal",
-            "Bright +",
-            "Bright -",
-            "Contrast +",
-            "Contrast -"
-        );
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        return getIndexedDescription(TAG_COLOR_MODE,
-            1,
-            "Color",
-            "Monochrome"
-        );
-    }
-
-    @Nullable
-    public String getQualityDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY,
-            1,
-            "VGA Basic",
-            "VGA Normal",
-            "VGA Fine",
-            "SXGA Basic",
-            "SXGA Normal",
-            "SXGA Fine"
-        );
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/NikonType1MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,93 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Nikon (type 1) cameras.  Type-1 is for E-Series cameras prior to (not including) E990.
- *
- * There are 3 formats of Nikon's Makernote. Makernote of E700/E800/E900/E900S/E910/E950
- * starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from
- * offset 0x08. This is the same as Olympus except start string. Example of actual data
- * structure is shown below.
- * <pre><code>
- * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
- * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
- * </code></pre>
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class NikonType1MakernoteDirectory extends Directory
-{
-    public static final int TAG_UNKNOWN_1 = 0x0002;
-    public static final int TAG_QUALITY = 0x0003;
-    public static final int TAG_COLOR_MODE = 0x0004;
-    public static final int TAG_IMAGE_ADJUSTMENT = 0x0005;
-    public static final int TAG_CCD_SENSITIVITY = 0x0006;
-    public static final int TAG_WHITE_BALANCE = 0x0007;
-    public static final int TAG_FOCUS = 0x0008;
-    public static final int TAG_UNKNOWN_2 = 0x0009;
-    public static final int TAG_DIGITAL_ZOOM = 0x000A;
-    public static final int TAG_CONVERTER = 0x000B;
-    public static final int TAG_UNKNOWN_3 = 0x0F00;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_CCD_SENSITIVITY, "CCD Sensitivity");
-        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_CONVERTER, "Fisheye Converter");
-        _tagNameMap.put(TAG_FOCUS, "Focus");
-        _tagNameMap.put(TAG_IMAGE_ADJUSTMENT, "Image Adjustment");
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_UNKNOWN_1, "Makernote Unknown 1");
-        _tagNameMap.put(TAG_UNKNOWN_2, "Makernote Unknown 2");
-        _tagNameMap.put(TAG_UNKNOWN_3, "Makernote Unknown 3");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-    }
-
-    public NikonType1MakernoteDirectory()
-    {
-        this.setDescriptor(new NikonType1MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Nikon Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,380 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.nio.ByteBuffer;
-import java.text.DecimalFormat;
-
-import static com.drew.metadata.exif.makernotes.NikonType2MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link NikonType2MakernoteDirectory}.
- *
- * Type-2 applies to the E990 and D-series cameras such as the D1, D70 and D100.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class NikonType2MakernoteDescriptor extends TagDescriptor<NikonType2MakernoteDirectory>
-{
-    public NikonType2MakernoteDescriptor(@NotNull NikonType2MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType)
-        {
-            case TAG_PROGRAM_SHIFT:
-                return getProgramShiftDescription();
-            case TAG_EXPOSURE_DIFFERENCE:
-                return getExposureDifferenceDescription();
-            case TAG_LENS:
-                return getLensDescription();
-            case TAG_CAMERA_HUE_ADJUSTMENT:
-                return getHueAdjustmentDescription();
-            case TAG_CAMERA_COLOR_MODE:
-                return getColorModeDescription();
-            case TAG_AUTO_FLASH_COMPENSATION:
-                return getAutoFlashCompensationDescription();
-            case TAG_FLASH_EXPOSURE_COMPENSATION:
-                return getFlashExposureCompensationDescription();
-            case TAG_FLASH_BRACKET_COMPENSATION:
-                return getFlashBracketCompensationDescription();
-            case TAG_EXPOSURE_TUNING:
-                return getExposureTuningDescription();
-            case TAG_LENS_STOPS:
-                return getLensStopsDescription();
-            case TAG_COLOR_SPACE:
-                return getColorSpaceDescription();
-            case TAG_ACTIVE_D_LIGHTING:
-                return getActiveDLightingDescription();
-            case TAG_VIGNETTE_CONTROL:
-                return getVignetteControlDescription();
-            case TAG_ISO_1:
-                return getIsoSettingDescription();
-            case TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case TAG_FLASH_USED:
-                return getFlashUsedDescription();
-            case TAG_AF_FOCUS_POSITION:
-                return getAutoFocusPositionDescription();
-            case TAG_FIRMWARE_VERSION:
-                return getFirmwareVersionDescription();
-            case TAG_LENS_TYPE:
-                return getLensTypeDescription();
-            case TAG_SHOOTING_MODE:
-                return getShootingModeDescription();
-            case TAG_NEF_COMPRESSION:
-                return getNEFCompressionDescription();
-            case TAG_HIGH_ISO_NOISE_REDUCTION:
-                return getHighISONoiseReductionDescription();
-            case TAG_POWER_UP_TIME:
-                return getPowerUpTimeDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getPowerUpTimeDescription()
-    {
-        // this is generally a byte[] of length 8 directly representing a date and time.
-        // the format is : first 2 bytes together are the year, and then each byte after
-        //                 is month, day, hour, minute, second with the eighth byte unused
-        // e.g., 2011:04:25 01:54:58
-
-        byte[] values = _directory.getByteArray(TAG_POWER_UP_TIME);
-        short year = ByteBuffer.wrap(new byte[]{values[0], values[1]}).getShort();
-        return String.format("%04d:%02d:%02d %02d:%02d:%02d", year, values[2], values[3], 
-                                                        values[4], values[5], values[6]);
-    }
-
-    @Nullable
-    public String getHighISONoiseReductionDescription()
-    {
-        return getIndexedDescription(TAG_HIGH_ISO_NOISE_REDUCTION,
-            "Off",
-            "Minimal",
-            "Low",
-            null,
-            "Normal",
-            null,
-            "High"
-        );
-    }
-
-    @Nullable
-    public String getFlashUsedDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_USED,
-            "Flash Not Used",
-            "Manual Flash",
-            null,
-            "Flash Not Ready",
-            null,
-            null,
-            null,
-            "External Flash",
-            "Fired, Commander Mode",
-            "Fired, TTL Mode"
-        );
-    }
-
-    @Nullable
-    public String getNEFCompressionDescription()
-    {
-        return getIndexedDescription(TAG_NEF_COMPRESSION,
-            1,
-            "Lossy (Type 1)",
-            null,
-            "Uncompressed",
-            null,
-            null,
-            null,
-            "Lossless",
-            "Lossy (Type 2)"
-        );
-    }
-
-    @Nullable
-    public String getShootingModeDescription()
-    {
-        return getBitFlagDescription(TAG_SHOOTING_MODE,
-            // LSB [low label, high label]
-            new String[]{"Single Frame", "Continuous"},
-            "Delay",
-            null,
-            "PC Control",
-            "Exposure Bracketing",
-            "Auto ISO",
-            "White-Balance Bracketing",
-            "IR Control"
-        );
-    }
-
-    @Nullable
-    public String getLensTypeDescription()
-    {
-        return getBitFlagDescription(TAG_LENS_TYPE,
-            // LSB [low label, high label]
-            new String[]{"AF", "MF"},
-            "D",
-            "G",
-            "VR"
-        );
-    }
-
-    @Nullable
-    public String getColorSpaceDescription()
-    {
-        return getIndexedDescription(TAG_COLOR_SPACE,
-            1,
-            "sRGB",
-            "Adobe RGB"
-        );
-    }
-
-    @Nullable
-    public String getActiveDLightingDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ACTIVE_D_LIGHTING);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "Light";
-            case 3: return "Normal";
-            case 5: return "High";
-            case 7: return "Extra High";
-            case 65535: return "Auto";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getVignetteControlDescription()
-    {
-        Integer value = _directory.getInteger(TAG_VIGNETTE_CONTROL);
-        if (value==null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "Low";
-            case 3: return "Normal";
-            case 5: return "High";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAutoFocusPositionDescription()
-    {
-        int[] values = _directory.getIntArray(TAG_AF_FOCUS_POSITION);
-        if (values==null)
-            return null;
-        if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) {
-            return "Unknown (" + _directory.getString(TAG_AF_FOCUS_POSITION) + ")";
-        }
-        switch (values[1]) {
-            case 0:
-                return "Centre";
-            case 1:
-                return "Top";
-            case 2:
-                return "Bottom";
-            case 3:
-                return "Left";
-            case 4:
-                return "Right";
-            default:
-                return "Unknown (" + values[1] + ")";
-        }
-    }
-
-    @Nullable
-    public String getDigitalZoomDescription()
-    {
-        Rational value = _directory.getRational(TAG_DIGITAL_ZOOM);
-        if (value==null)
-            return null;
-        return value.intValue() == 1
-                ? "No digital zoom"
-                : value.toSimpleString(true) + "x digital zoom";
-    }
-
-    @Nullable
-    public String getProgramShiftDescription()
-    {
-        return getEVDescription(TAG_PROGRAM_SHIFT);
-    }
-
-    @Nullable
-    public String getExposureDifferenceDescription()
-    {
-        return getEVDescription(TAG_EXPOSURE_DIFFERENCE);
-    }
-
-    @Nullable
-    public String getAutoFlashCompensationDescription()
-    {
-        return getEVDescription(TAG_AUTO_FLASH_COMPENSATION);
-    }
-
-    @Nullable
-    public String getFlashExposureCompensationDescription()
-    {
-        return getEVDescription(TAG_FLASH_EXPOSURE_COMPENSATION);
-    }
-
-    @Nullable
-    public String getFlashBracketCompensationDescription()
-    {
-        return getEVDescription(TAG_FLASH_BRACKET_COMPENSATION);
-    }
-
-    @Nullable
-    public String getExposureTuningDescription()
-    {
-        return getEVDescription(TAG_EXPOSURE_TUNING);
-    }
-
-    @Nullable
-    public String getLensStopsDescription()
-    {
-        return getEVDescription(TAG_LENS_STOPS);
-    }
-
-    @Nullable
-    private String getEVDescription(int tagType)
-    {
-        int[] values = _directory.getIntArray(tagType);
-        if (values == null || values.length < 2)
-            return null;
-        if (values.length < 3 || values[2] == 0)
-            return null;
-        final DecimalFormat decimalFormat = new DecimalFormat("0.##");
-        double ev = values[0] * values[1] / (double)values[2];
-        return decimalFormat.format(ev) + " EV";
-    }
-
-    @Nullable
-    public String getIsoSettingDescription()
-    {
-        int[] values = _directory.getIntArray(TAG_ISO_1);
-        if (values == null)
-            return null;
-        if (values[0] != 0 || values[1] == 0)
-            return "Unknown (" + _directory.getString(TAG_ISO_1) + ")";
-        return "ISO " + values[1];
-    }
-
-    @Nullable
-    public String getLensDescription()
-    {
-        return getLensSpecificationDescription(TAG_LENS);
-    }
-
-    @Nullable
-    public String getLensFocusDistance()
-    {
-        int[] values = _directory.getDecryptedIntArray(TAG_LENS_DATA);
-
-        if (values == null || values.length < 11)
-            return null;
-
-        return String.format("%.2fm", getDistanceInMeters(values[10]));
-    }
-
-    @Nullable
-    public String getHueAdjustmentDescription()
-    {
-        return getFormattedString(TAG_CAMERA_HUE_ADJUSTMENT, "%s degrees");
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        String value = _directory.getString(TAG_CAMERA_COLOR_MODE);
-        return value == null ? null : value.startsWith("MODE1") ? "Mode I (sRGB)" : value;
-    }
-
-    @Nullable
-    public String getFirmwareVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_FIRMWARE_VERSION, 2);
-    }
-
-    private double getDistanceInMeters(int val)
-    {
-        if (val < 0)
-            val += 256;
-        return 0.01 * Math.pow(10, val / 40.0f);
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/NikonType2MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,990 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.Nullable;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Nikon (type 2) cameras.  Type-2 applies to the E990 and D-series cameras such as the E990, D1,
- * D70 and D100.
- * <p>
- * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data.
- * http://www.timelesswanderings.net/equipment/D100/NEF.html
- * <p>
- * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute.  Similarly
- * image hiding uses the 'hidden' attribute (observed on the D70).  Consequently, these values are not available here.
- * <p>
- * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field.
- * New tags have subsequently been added since Fabrizio's observations.
- * <p>
- * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD.  In
- * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also
- * provide this prefix to aid in software decoding).
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class NikonType2MakernoteDirectory extends Directory
-{
-    /**
-     * Values observed
-     * - 0200 (D70)
-     * - 0200 (D1X)
-     */
-    public static final int TAG_FIRMWARE_VERSION = 0x0001;
-
-    /**
-     * Values observed
-     * - 0 250
-     * - 0 400
-     */
-    public static final int TAG_ISO_1 = 0x0002;
-
-    /**
-     * The camera's color mode, as an uppercase string.  Examples include:
-     * <ul>
-     * <li><code>B &amp; W</code></li>
-     * <li><code>COLOR</code></li>
-     * <li><code>COOL</code></li>
-     * <li><code>SEPIA</code></li>
-     * <li><code>VIVID</code></li>
-     * </ul>
-     */
-    public static final int TAG_COLOR_MODE = 0x0003;
-
-    /**
-     * The camera's quality setting, as an uppercase string.  Examples include:
-     * <ul>
-     * <li><code>BASIC</code></li>
-     * <li><code>FINE</code></li>
-     * <li><code>NORMAL</code></li>
-     * <li><code>RAW</code></li>
-     * <li><code>RAW2.7M</code></li>
-     * </ul>
-     */
-    public static final int TAG_QUALITY_AND_FILE_FORMAT = 0x0004;
-
-    /**
-     * The camera's white balance setting, as an uppercase string.  Examples include:
-     *
-     * <ul>
-     * <li><code>AUTO</code></li>
-     * <li><code>CLOUDY</code></li>
-     * <li><code>FLASH</code></li>
-     * <li><code>FLUORESCENT</code></li>
-     * <li><code>INCANDESCENT</code></li>
-     * <li><code>PRESET</code></li>
-     * <li><code>PRESET0</code></li>
-     * <li><code>PRESET1</code></li>
-     * <li><code>PRESET3</code></li>
-     * <li><code>SUNNY</code></li>
-     * <li><code>WHITE PRESET</code></li>
-     * <li><code>4350K</code></li>
-     * <li><code>5000K</code></li>
-     * <li><code>DAY WHITE FL</code></li>
-     * <li><code>SHADE</code></li>
-     * </ul>
-     */
-    public static final int TAG_CAMERA_WHITE_BALANCE  = 0x0005;
-
-    /**
-     * The camera's sharpening setting, as an uppercase string.  Examples include:
-     *
-     * <ul>
-     * <li><code>AUTO</code></li>
-     * <li><code>HIGH</code></li>
-     * <li><code>LOW</code></li>
-     * <li><code>NONE</code></li>
-     * <li><code>NORMAL</code></li>
-     * <li><code>MED.H</code></li>
-     * <li><code>MED.L</code></li>
-     * </ul>
-     */
-    public static final int TAG_CAMERA_SHARPENING = 0x0006;
-
-    /**
-     * The camera's auto-focus mode, as an uppercase string.  Examples include:
-     *
-     * <ul>
-     * <li><code>AF-C</code></li>
-     * <li><code>AF-S</code></li>
-     * <li><code>MANUAL</code></li>
-     * <li><code>AF-A</code></li>
-     * </ul>
-     */
-    public static final int TAG_AF_TYPE = 0x0007;
-
-    /**
-     * The camera's flash setting, as an uppercase string.  Examples include:
-     *
-     * <ul>
-     * <li><code></code></li>
-     * <li><code>NORMAL</code></li>
-     * <li><code>RED-EYE</code></li>
-     * <li><code>SLOW</code></li>
-     * <li><code>NEW_TTL</code></li>
-     * <li><code>REAR</code></li>
-     * <li><code>REAR SLOW</code></li>
-     * </ul>
-     * Note: when TAG_AUTO_FLASH_MODE is blank (whitespace), Nikon Browser displays "Flash Sync Mode: Not Attached"
-     */
-    public static final int TAG_FLASH_SYNC_MODE = 0x0008;
-
-    /**
-     * The type of flash used in the photograph, as a string.  Examples include:
-     *
-     * <ul>
-     * <li><code></code></li>
-     * <li><code>Built-in,TTL</code></li>
-     * <li><code>NEW_TTL</code> Nikon Browser interprets as "D-TTL"</li>
-     * <li><code>Built-in,M</code></li>
-     * <li><code>Optional,TTL</code> with speedlight SB800, flash sync mode as "NORMAL"</li>
-     * </ul>
-     */
-    public static final int TAG_AUTO_FLASH_MODE = 0x0009;
-
-    /**
-     * An unknown tag, as a rational.  Several values given here:
-     * http://gvsoft.homedns.org/exif/makernote-nikon-type2.html#0x000b
-     */
-    public static final int TAG_UNKNOWN_34 = 0x000A;
-
-    /**
-     * The camera's white balance bias setting, as an uint16 array having either one or two elements.
-     *
-     * <ul>
-     * <li><code>0</code></li>
-     * <li><code>1</code></li>
-     * <li><code>-3</code></li>
-     * <li><code>-2</code></li>
-     * <li><code>-1</code></li>
-     * <li><code>0,0</code></li>
-     * <li><code>1,0</code></li>
-     * <li><code>5,-5</code></li>
-     * </ul>
-     */
-    public static final int TAG_CAMERA_WHITE_BALANCE_FINE = 0x000B;
-
-    /**
-     * The first two numbers are coefficients to multiply red and blue channels according to white balance as set in the
-     * camera. The meaning of the third and the fourth numbers is unknown.
-     *
-     * Values observed
-     * - 2.25882352 1.76078431 0.0 0.0
-     * - 10242/1 34305/1 0/1 0/1
-     * - 234765625/100000000 1140625/1000000 1/1 1/1
-     */
-    public static final int TAG_CAMERA_WHITE_BALANCE_RB_COEFF = 0x000C;
-
-    /**
-     * The camera's program shift setting, as an array of four integers.
-     * The value, in EV, is calculated as <code>a*b/c</code>.
-     *
-     * <ul>
-     * <li><code>0,1,3,0</code> = 0 EV</li>
-     * <li><code>1,1,3,0</code> = 0.33 EV</li>
-     * <li><code>-3,1,3,0</code> = -1 EV</li>
-     * <li><code>1,1,2,0</code> = 0.5 EV</li>
-     * <li><code>2,1,6,0</code> = 0.33 EV</li>
-     * </ul>
-     */
-    public static final int TAG_PROGRAM_SHIFT = 0x000D;
-
-    /**
-     * The exposure difference, as an array of four integers.
-     * The value, in EV, is calculated as <code>a*b/c</code>.
-     *
-     * <ul>
-     * <li><code>-105,1,12,0</code> = -8.75 EV</li>
-     * <li><code>-72,1,12,0</code> = -6.00 EV</li>
-     * <li><code>-11,1,12,0</code> = -0.92 EV</li>
-     * </ul>
-     */
-    public static final int TAG_EXPOSURE_DIFFERENCE = 0x000E;
-
-    /**
-     * The camera's ISO mode, as an uppercase string.
-     *
-     * <ul>
-     * <li><code>AUTO</code></li>
-     * <li><code>MANUAL</code></li>
-     * </ul>
-     */
-    public static final int TAG_ISO_MODE = 0x000F;
-
-    /**
-     * Added during merge of Type2 &amp; Type3.  May apply to earlier models, such as E990 and D1.
-     */
-    public static final int TAG_DATA_DUMP = 0x0010;
-
-    /**
-     * Preview to another IFD (?)
-     * <p>
-     * Details here: http://gvsoft.homedns.org/exif/makernote-nikon-2-tag0x0011.html
-     * // TODO if this is another IFD, decode it
-     */
-    public static final int TAG_PREVIEW_IFD = 0x0011;
-
-    /**
-     * The flash compensation, as an array of four integers.
-     * The value, in EV, is calculated as <code>a*b/c</code>.
-     *
-     * <ul>
-     * <li><code>-18,1,6,0</code> = -3 EV</li>
-     * <li><code>4,1,6,0</code> = 0.67 EV</li>
-     * <li><code>6,1,6,0</code> = 1 EV</li>
-     * </ul>
-     */
-    public static final int TAG_AUTO_FLASH_COMPENSATION = 0x0012;
-
-    /**
-     * The requested ISO value, as an array of two integers.
-     *
-     * <ul>
-     * <li><code>0,0</code></li>
-     * <li><code>0,125</code></li>
-     * <li><code>1,2500</code></li>
-     * </ul>
-     */
-    public static final int TAG_ISO_REQUESTED = 0x0013;
-
-    /**
-     * Defines the photo corner coordinates, in 8 bytes.  Treated as four 16-bit integers, they
-     * decode as: top-left (x,y); bot-right (x,y)
-     * - 0 0 49163 53255
-     * - 0 0 3008 2000 (the image dimensions were 3008x2000) (D70)
-     * <ul>
-     * <li><code>0,0,4288,2848</code> The max resolution of the D300 camera</li>
-     * <li><code>0,0,3008,2000</code> The max resolution of the D70 camera</li>
-     * <li><code>0,0,4256,2832</code> The max resolution of the D3 camera</li>
-     * </ul>
-     */
-    public static final int TAG_IMAGE_BOUNDARY = 0x0016;
-
-    /**
-     * The flash exposure compensation, as an array of four integers.
-     * The value, in EV, is calculated as <code>a*b/c</code>.
-     *
-     * <ul>
-     * <li><code>0,0,0,0</code> = 0 EV</li>
-     * <li><code>0,1,6,0</code> = 0 EV</li>
-     * <li><code>4,1,6,0</code> = 0.67 EV</li>
-     * </ul>
-     */
-    public static final int TAG_FLASH_EXPOSURE_COMPENSATION = 0x0017;
-
-    /**
-     * The flash bracket compensation, as an array of four integers.
-     * The value, in EV, is calculated as <code>a*b/c</code>.
-     *
-     * <ul>
-     * <li><code>0,0,0,0</code> = 0 EV</li>
-     * <li><code>0,1,6,0</code> = 0 EV</li>
-     * <li><code>4,1,6,0</code> = 0.67 EV</li>
-     * </ul>
-     */
-    public static final int TAG_FLASH_BRACKET_COMPENSATION = 0x0018;
-
-    /**
-     * The AE bracket compensation, as a rational number.
-     *
-     * <ul>
-     * <li><code>0/0</code></li>
-     * <li><code>0/1</code></li>
-     * <li><code>0/6</code></li>
-     * <li><code>4/6</code></li>
-     * <li><code>6/6</code></li>
-     * </ul>
-     */
-    public static final int TAG_AE_BRACKET_COMPENSATION = 0x0019;
-
-    /**
-     * Flash mode, as a string.
-     *
-     * <ul>
-     * <li><code></code></li>
-     * <li><code>Red Eye Reduction</code></li>
-     * <li><code>D-Lighting</code></li>
-     * <li><code>Distortion control</code></li>
-     * </ul>
-     */
-    public static final int TAG_FLASH_MODE = 0x001a;
-
-    public static final int TAG_CROP_HIGH_SPEED = 0x001b;
-    public static final int TAG_EXPOSURE_TUNING = 0x001c;
-
-    /**
-     * The camera's serial number, as a string.
-     * Note that D200 is always blank, and D50 is always <code>"D50"</code>.
-     */
-    public static final int TAG_CAMERA_SERIAL_NUMBER = 0x001d;
-
-    /**
-     * The camera's color space setting.
-     *
-     * <ul>
-     * <li><code>1</code> sRGB</li>
-     * <li><code>2</code> Adobe RGB</li>
-     * </ul>
-     */
-    public static final int TAG_COLOR_SPACE = 0x001e;
-    public static final int TAG_VR_INFO = 0x001f;
-    public static final int TAG_IMAGE_AUTHENTICATION = 0x0020;
-    public static final int TAG_UNKNOWN_35 = 0x0021;
-
-    /**
-     * The active D-Lighting setting.
-     *
-     * <ul>
-     * <li><code>0</code> Off</li>
-     * <li><code>1</code> Low</li>
-     * <li><code>3</code> Normal</li>
-     * <li><code>5</code> High</li>
-     * <li><code>7</code> Extra High</li>
-     * <li><code>65535</code> Auto</li>
-     * </ul>
-     */
-    public static final int TAG_ACTIVE_D_LIGHTING = 0x0022;
-    public static final int TAG_PICTURE_CONTROL = 0x0023;
-    public static final int TAG_WORLD_TIME = 0x0024;
-    public static final int TAG_ISO_INFO = 0x0025;
-    public static final int TAG_UNKNOWN_36 = 0x0026;
-    public static final int TAG_UNKNOWN_37 = 0x0027;
-    public static final int TAG_UNKNOWN_38 = 0x0028;
-    public static final int TAG_UNKNOWN_39 = 0x0029;
-
-    /**
-     * The camera's vignette control setting.
-     *
-     * <ul>
-     * <li><code>0</code> Off</li>
-     * <li><code>1</code> Low</li>
-     * <li><code>3</code> Normal</li>
-     * <li><code>5</code> High</li>
-     * </ul>
-     */
-    public static final int TAG_VIGNETTE_CONTROL = 0x002a;
-    public static final int TAG_UNKNOWN_40 = 0x002b;
-    public static final int TAG_UNKNOWN_41 = 0x002c;
-    public static final int TAG_UNKNOWN_42 = 0x002d;
-    public static final int TAG_UNKNOWN_43 = 0x002e;
-    public static final int TAG_UNKNOWN_44 = 0x002f;
-    public static final int TAG_UNKNOWN_45 = 0x0030;
-    public static final int TAG_UNKNOWN_46 = 0x0031;
-
-    /**
-     * The camera's image adjustment setting, as a string.
-     *
-     * <ul>
-     * <li><code>AUTO</code></li>
-     * <li><code>CONTRAST(+)</code></li>
-     * <li><code>CONTRAST(-)</code></li>
-     * <li><code>NORMAL</code></li>
-     * <li><code>B &amp; W</code></li>
-     * <li><code>BRIGHTNESS(+)</code></li>
-     * <li><code>BRIGHTNESS(-)</code></li>
-     * <li><code>SEPIA</code></li>
-     * </ul>
-     */
-    public static final int TAG_IMAGE_ADJUSTMENT = 0x0080;
-
-    /**
-     * The camera's tone compensation setting, as a string.
-     *
-     * <ul>
-     * <li><code>NORMAL</code></li>
-     * <li><code>LOW</code></li>
-     * <li><code>MED.L</code></li>
-     * <li><code>MED.H</code></li>
-     * <li><code>HIGH</code></li>
-     * <li><code>AUTO</code></li>
-     * </ul>
-     */
-    public static final int TAG_CAMERA_TONE_COMPENSATION = 0x0081;
-
-    /**
-     * A description of any auxiliary lens, as a string.
-     *
-     * <ul>
-     * <li><code>OFF</code></li>
-     * <li><code>FISHEYE 1</code></li>
-     * <li><code>FISHEYE 2</code></li>
-     * <li><code>TELEPHOTO 2</code></li>
-     * <li><code>WIDE ADAPTER</code></li>
-     * </ul>
-     */
-    public static final int TAG_ADAPTER = 0x0082;
-
-    /**
-     * The type of lens used, as a byte.
-     *
-     * <ul>
-     * <li><code>0x00</code> AF</li>
-     * <li><code>0x01</code> MF</li>
-     * <li><code>0x02</code> D</li>
-     * <li><code>0x06</code> G, D</li>
-     * <li><code>0x08</code> VR</li>
-     * <li><code>0x0a</code> VR, D</li>
-     * <li><code>0x0e</code> VR, G, D</li>
-     * </ul>
-     */
-    public static final int TAG_LENS_TYPE = 0x0083;
-
-    /**
-     * A pair of focal/max-fstop values that describe the lens used.
-     *
-     * Values observed
-     * - 180.0,180.0,2.8,2.8 (D100)
-     * - 240/10 850/10 35/10 45/10
-     * - 18-70mm f/3.5-4.5 (D70)
-     * - 17-35mm f/2.8-2.8 (D1X)
-     * - 70-200mm f/2.8-2.8 (D70)
-     *
-     * Nikon Browser identifies the lens as "18-70mm F/3.5-4.5 G" which
-     * is identical to metadata extractor, except for the "G".  This must
-     * be coming from another tag...
-     */
-    public static final int TAG_LENS = 0x0084;
-
-    /**
-     * Added during merge of Type2 &amp; Type3.  May apply to earlier models, such as E990 and D1.
-     */
-    public static final int TAG_MANUAL_FOCUS_DISTANCE = 0x0085;
-
-    /**
-     * The amount of digital zoom used.
-     */
-    public static final int TAG_DIGITAL_ZOOM = 0x0086;
-
-    /**
-     * Whether the flash was used in this image.
-     *
-     * <ul>
-     * <li><code>0</code> Flash Not Used</li>
-     * <li><code>1</code> Manual Flash</li>
-     * <li><code>3</code> Flash Not Ready</li>
-     * <li><code>7</code> External Flash</li>
-     * <li><code>8</code> Fired, Commander Mode</li>
-     * <li><code>9</code> Fired, TTL Mode</li>
-     * </ul>
-     */
-    public static final int TAG_FLASH_USED = 0x0087;
-
-    /**
-     * The position of the autofocus target.
-     */
-    public static final int TAG_AF_FOCUS_POSITION = 0x0088;
-
-    /**
-     * The camera's shooting mode.
-     * <p>
-     * A bit-array with:
-     * <ul>
-     * <li><code>0</code> Single Frame</li>
-     * <li><code>1</code> Continuous</li>
-     * <li><code>2</code> Delay</li>
-     * <li><code>8</code> PC Control</li>
-     * <li><code>16</code> Exposure Bracketing</li>
-     * <li><code>32</code> Auto ISO</li>
-     * <li><code>64</code> White-Balance Bracketing</li>
-     * <li><code>128</code> IR Control</li>
-     * </ul>
-     */
-    public static final int TAG_SHOOTING_MODE = 0x0089;
-
-    public static final int TAG_UNKNOWN_20 = 0x008A;
-
-    /**
-     * Lens stops, as an array of four integers.
-     * The value, in EV, is calculated as <code>a*b/c</code>.
-     *
-     * <ul>
-     * <li><code>64,1,12,0</code> = 5.33 EV</li>
-     * <li><code>72,1,12,0</code> = 6 EV</li>
-     * </ul>
-     */
-    public static final int TAG_LENS_STOPS = 0x008B;
-
-    public static final int TAG_CONTRAST_CURVE = 0x008C;
-
-    /**
-     * The color space as set in the camera, as a string.
-     *
-     * <ul>
-     * <li><code>MODE1</code> = Mode 1 (sRGB)</li>
-     * <li><code>MODE1a</code> = Mode 1 (sRGB)</li>
-     * <li><code>MODE2</code> = Mode 2 (Adobe RGB)</li>
-     * <li><code>MODE3</code> = Mode 2 (sRGB): Higher Saturation</li>
-     * <li><code>MODE3a</code> = Mode 2 (sRGB): Higher Saturation</li>
-     * <li><code>B &amp; W</code> = B &amp; W</li>
-     * </ul>
-     */
-    public static final int TAG_CAMERA_COLOR_MODE = 0x008D;
-    public static final int TAG_UNKNOWN_47 = 0x008E;
-
-    /**
-     * The camera's scene mode, as a string.  Examples include:
-     * <ul>
-     * <li><code>BEACH/SNOW</code></li>
-     * <li><code>CLOSE UP</code></li>
-     * <li><code>NIGHT PORTRAIT</code></li>
-     * <li><code>PORTRAIT</code></li>
-     * <li><code>ANTI-SHAKE</code></li>
-     * <li><code>BACK LIGHT</code></li>
-     * <li><code>BEST FACE</code></li>
-     * <li><code>BEST</code></li>
-     * <li><code>COPY</code></li>
-     * <li><code>DAWN/DUSK</code></li>
-     * <li><code>FACE-PRIORITY</code></li>
-     * <li><code>FIREWORKS</code></li>
-     * <li><code>FOOD</code></li>
-     * <li><code>HIGH SENS.</code></li>
-     * <li><code>LAND SCAPE</code></li>
-     * <li><code>MUSEUM</code></li>
-     * <li><code>PANORAMA ASSIST</code></li>
-     * <li><code>PARTY/INDOOR</code></li>
-     * <li><code>SCENE AUTO</code></li>
-     * <li><code>SMILE</code></li>
-     * <li><code>SPORT</code></li>
-     * <li><code>SPORT CONT.</code></li>
-     * <li><code>SUNSET</code></li>
-     * </ul>
-     */
-    public static final int TAG_SCENE_MODE = 0x008F;
-
-    /**
-     * The lighting type, as a string.  Examples include:
-     * <ul>
-     * <li><code></code></li>
-     * <li><code>NATURAL</code></li>
-     * <li><code>SPEEDLIGHT</code></li>
-     * <li><code>COLORED</code></li>
-     * <li><code>MIXED</code></li>
-     * <li><code>NORMAL</code></li>
-     * </ul>
-     */
-    public static final int TAG_LIGHT_SOURCE = 0x0090;
-
-    /**
-     * Advertised as ASCII, but actually isn't.  A variable number of bytes (eg. 18 to 533).  Actual number of bytes
-     * appears fixed for a given camera model.
-     */
-    public static final int TAG_SHOT_INFO = 0x0091;
-
-    /**
-     * The hue adjustment as set in the camera.  Values observed are either 0 or 3.
-     */
-    public static final int TAG_CAMERA_HUE_ADJUSTMENT = 0x0092;
-    /**
-     * The NEF (RAW) compression.  Examples include:
-     * <ul>
-     * <li><code>1</code> Lossy (Type 1)</li>
-     * <li><code>2</code> Uncompressed</li>
-     * <li><code>3</code> Lossless</li>
-     * <li><code>4</code> Lossy (Type 2)</li>
-     * </ul>
-     */
-    public static final int TAG_NEF_COMPRESSION = 0x0093;
-
-    /**
-     * The saturation level, as a signed integer.  Examples include:
-     * <ul>
-     * <li><code>+3</code></li>
-     * <li><code>+2</code></li>
-     * <li><code>+1</code></li>
-     * <li><code>0</code> Normal</li>
-     * <li><code>-1</code></li>
-     * <li><code>-2</code></li>
-     * <li><code>-3</code> (B&amp;W)</li>
-     * </ul>
-     */
-    public static final int TAG_SATURATION = 0x0094;
-
-    /**
-     * The type of noise reduction, as a string.  Examples include:
-     * <ul>
-     * <li><code>OFF</code></li>
-     * <li><code>FPNR</code></li>
-     * </ul>
-     */
-    public static final int TAG_NOISE_REDUCTION = 0x0095;
-    public static final int TAG_LINEARIZATION_TABLE = 0x0096;
-    public static final int TAG_COLOR_BALANCE = 0x0097;
-    public static final int TAG_LENS_DATA = 0x0098;
-
-    /** The NEF (RAW) thumbnail size, as an integer array with two items representing [width,height]. */
-    public static final int TAG_NEF_THUMBNAIL_SIZE = 0x0099;
-
-    /** The sensor pixel size, as a pair of rational numbers. */
-    public static final int TAG_SENSOR_PIXEL_SIZE = 0x009A;
-    public static final int TAG_UNKNOWN_10 = 0x009B;
-    public static final int TAG_SCENE_ASSIST = 0x009C;
-    public static final int TAG_UNKNOWN_11 = 0x009D;
-    public static final int TAG_RETOUCH_HISTORY = 0x009E;
-    public static final int TAG_UNKNOWN_12 = 0x009F;
-
-    /**
-     * The camera serial number, as a string.
-     * <ul>
-     * <li><code>NO= 00002539</code></li>
-     * <li><code>NO= -1000d71</code></li>
-     * <li><code>PKG597230621263</code></li>
-     * <li><code>PKG5995671330625116</code></li>
-     * <li><code>PKG49981281631130677</code></li>
-     * <li><code>BU672230725063</code></li>
-     * <li><code>NO= 200332c7</code></li>
-     * <li><code>NO= 30045efe</code></li>
-     * </ul>
-     */
-    public static final int TAG_CAMERA_SERIAL_NUMBER_2 = 0x00A0;
-
-    public static final int TAG_IMAGE_DATA_SIZE = 0x00A2;
-
-    public static final int TAG_UNKNOWN_27 = 0x00A3;
-    public static final int TAG_UNKNOWN_28 = 0x00A4;
-    public static final int TAG_IMAGE_COUNT = 0x00A5;
-    public static final int TAG_DELETED_IMAGE_COUNT = 0x00A6;
-
-    /** The number of total shutter releases.  This value increments for each exposure (observed on D70). */
-    public static final int TAG_EXPOSURE_SEQUENCE_NUMBER = 0x00A7;
-
-    public static final int TAG_FLASH_INFO = 0x00A8;
-    /**
-     * The camera's image optimisation, as a string.
-     * <ul>
-     *     <li><code></code></li>
-     *     <li><code>NORMAL</code></li>
-     *     <li><code>CUSTOM</code></li>
-     *     <li><code>BLACK AND WHITE</code></li>
-     *     <li><code>LAND SCAPE</code></li>
-     *     <li><code>MORE VIVID</code></li>
-     *     <li><code>PORTRAIT</code></li>
-     *     <li><code>SOFT</code></li>
-     *     <li><code>VIVID</code></li>
-     * </ul>
-     */
-    public static final int TAG_IMAGE_OPTIMISATION = 0x00A9;
-
-    /**
-     * The camera's saturation level, as a string.
-     * <ul>
-     *     <li><code></code></li>
-     *     <li><code>NORMAL</code></li>
-     *     <li><code>AUTO</code></li>
-     *     <li><code>ENHANCED</code></li>
-     *     <li><code>MODERATE</code></li>
-     * </ul>
-     */
-    public static final int TAG_SATURATION_2 = 0x00AA;
-
-    /**
-     * The camera's digital vari-program setting, as a string.
-     * <ul>
-     *     <li><code></code></li>
-     *     <li><code>AUTO</code></li>
-     *     <li><code>AUTO(FLASH OFF)</code></li>
-     *     <li><code>CLOSE UP</code></li>
-     *     <li><code>LANDSCAPE</code></li>
-     *     <li><code>NIGHT PORTRAIT</code></li>
-     *     <li><code>PORTRAIT</code></li>
-     *     <li><code>SPORT</code></li>
-     * </ul>
-     */
-    public static final int TAG_DIGITAL_VARI_PROGRAM = 0x00AB;
-
-    /**
-     * The camera's digital vari-program setting, as a string.
-     * <ul>
-     *     <li><code></code></li>
-     *     <li><code>VR-ON</code></li>
-     *     <li><code>VR-OFF</code></li>
-     *     <li><code>VR-HYBRID</code></li>
-     *     <li><code>VR-ACTIVE</code></li>
-     * </ul>
-     */
-    public static final int TAG_IMAGE_STABILISATION = 0x00AC;
-
-    /**
-     * The camera's digital vari-program setting, as a string.
-     * <ul>
-     *     <li><code></code></li>
-     *     <li><code>HYBRID</code></li>
-     *     <li><code>STANDARD</code></li>
-     * </ul>
-     */
-    public static final int TAG_AF_RESPONSE = 0x00AD;
-    public static final int TAG_UNKNOWN_29 = 0x00AE;
-    public static final int TAG_UNKNOWN_30 = 0x00AF;
-    public static final int TAG_MULTI_EXPOSURE = 0x00B0;
-
-    /**
-     * The camera's high ISO noise reduction setting, as an integer.
-     * <ul>
-     *     <li><code>0</code> Off</li>
-     *     <li><code>1</code> Minimal</li>
-     *     <li><code>2</code> Low</li>
-     *     <li><code>4</code> Normal</li>
-     *     <li><code>6</code> High</li>
-     * </ul>
-     */
-    public static final int TAG_HIGH_ISO_NOISE_REDUCTION = 0x00B1;
-    public static final int TAG_UNKNOWN_31 = 0x00B2;
-    public static final int TAG_UNKNOWN_32 = 0x00B3;
-    public static final int TAG_UNKNOWN_33 = 0x00B4;
-    public static final int TAG_UNKNOWN_48 = 0x00B5;
-    public static final int TAG_POWER_UP_TIME = 0x00B6;
-    public static final int TAG_AF_INFO_2 = 0x00B7;
-    public static final int TAG_FILE_INFO = 0x00B8;
-    public static final int TAG_AF_TUNE = 0x00B9;
-    public static final int TAG_UNKNOWN_49 = 0x00BB;
-    public static final int TAG_UNKNOWN_50 = 0x00BD;
-    public static final int TAG_UNKNOWN_51 = 0x0103;
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    /**
-     * Data about changes set by Nikon Capture Editor.
-     *
-     * Values observed
-     */
-    public static final int TAG_NIKON_CAPTURE_DATA = 0x0E01;
-    public static final int TAG_UNKNOWN_52 = 0x0E05;
-    public static final int TAG_UNKNOWN_53 = 0x0E08;
-    public static final int TAG_NIKON_CAPTURE_VERSION = 0x0E09;
-    public static final int TAG_NIKON_CAPTURE_OFFSETS = 0x0E0E;
-    public static final int TAG_NIKON_SCAN = 0x0E10;
-    public static final int TAG_UNKNOWN_54 = 0x0E19;
-    public static final int TAG_NEF_BIT_DEPTH = 0x0E22;
-    public static final int TAG_UNKNOWN_55 = 0x0E23;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_FIRMWARE_VERSION, "Firmware Version");
-        _tagNameMap.put(TAG_ISO_1, "ISO");
-        _tagNameMap.put(TAG_QUALITY_AND_FILE_FORMAT, "Quality & File Format");
-        _tagNameMap.put(TAG_CAMERA_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_CAMERA_SHARPENING, "Sharpening");
-        _tagNameMap.put(TAG_AF_TYPE, "AF Type");
-        _tagNameMap.put(TAG_CAMERA_WHITE_BALANCE_FINE, "White Balance Fine");
-        _tagNameMap.put(TAG_CAMERA_WHITE_BALANCE_RB_COEFF, "White Balance RB Coefficients");
-        _tagNameMap.put(TAG_ISO_REQUESTED, "ISO");
-        _tagNameMap.put(TAG_ISO_MODE, "ISO Mode");
-        _tagNameMap.put(TAG_DATA_DUMP, "Data Dump");
-
-        _tagNameMap.put(TAG_PROGRAM_SHIFT, "Program Shift");
-        _tagNameMap.put(TAG_EXPOSURE_DIFFERENCE, "Exposure Difference");
-        _tagNameMap.put(TAG_PREVIEW_IFD, "Preview IFD");
-        _tagNameMap.put(TAG_LENS_TYPE, "Lens Type");
-        _tagNameMap.put(TAG_FLASH_USED, "Flash Used");
-        _tagNameMap.put(TAG_AF_FOCUS_POSITION, "AF Focus Position");
-        _tagNameMap.put(TAG_SHOOTING_MODE, "Shooting Mode");
-        _tagNameMap.put(TAG_LENS_STOPS, "Lens Stops");
-        _tagNameMap.put(TAG_CONTRAST_CURVE, "Contrast Curve");
-        _tagNameMap.put(TAG_LIGHT_SOURCE, "Light source");
-        _tagNameMap.put(TAG_SHOT_INFO, "Shot Info");
-        _tagNameMap.put(TAG_COLOR_BALANCE, "Color Balance");
-        _tagNameMap.put(TAG_LENS_DATA, "Lens Data");
-        _tagNameMap.put(TAG_NEF_THUMBNAIL_SIZE, "NEF Thumbnail Size");
-        _tagNameMap.put(TAG_SENSOR_PIXEL_SIZE, "Sensor Pixel Size");
-        _tagNameMap.put(TAG_UNKNOWN_10, "Unknown 10");
-        _tagNameMap.put(TAG_SCENE_ASSIST, "Scene Assist");
-        _tagNameMap.put(TAG_UNKNOWN_11, "Unknown 11");
-        _tagNameMap.put(TAG_RETOUCH_HISTORY, "Retouch History");
-        _tagNameMap.put(TAG_UNKNOWN_12, "Unknown 12");
-        _tagNameMap.put(TAG_FLASH_SYNC_MODE, "Flash Sync Mode");
-        _tagNameMap.put(TAG_AUTO_FLASH_MODE, "Auto Flash Mode");
-        _tagNameMap.put(TAG_AUTO_FLASH_COMPENSATION, "Auto Flash Compensation");
-        _tagNameMap.put(TAG_EXPOSURE_SEQUENCE_NUMBER, "Exposure Sequence Number");
-        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
-
-        _tagNameMap.put(TAG_UNKNOWN_20, "Unknown 20");
-        _tagNameMap.put(TAG_IMAGE_BOUNDARY, "Image Boundary");
-        _tagNameMap.put(TAG_FLASH_EXPOSURE_COMPENSATION, "Flash Exposure Compensation");
-        _tagNameMap.put(TAG_FLASH_BRACKET_COMPENSATION, "Flash Bracket Compensation");
-        _tagNameMap.put(TAG_AE_BRACKET_COMPENSATION, "AE Bracket Compensation");
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(TAG_CROP_HIGH_SPEED, "Crop High Speed");
-        _tagNameMap.put(TAG_EXPOSURE_TUNING, "Exposure Tuning");
-        _tagNameMap.put(TAG_CAMERA_SERIAL_NUMBER, "Camera Serial Number");
-        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
-        _tagNameMap.put(TAG_VR_INFO, "VR Info");
-        _tagNameMap.put(TAG_IMAGE_AUTHENTICATION, "Image Authentication");
-        _tagNameMap.put(TAG_UNKNOWN_35, "Unknown 35");
-        _tagNameMap.put(TAG_ACTIVE_D_LIGHTING, "Active D-Lighting");
-        _tagNameMap.put(TAG_PICTURE_CONTROL, "Picture Control");
-        _tagNameMap.put(TAG_WORLD_TIME, "World Time");
-        _tagNameMap.put(TAG_ISO_INFO, "ISO Info");
-        _tagNameMap.put(TAG_UNKNOWN_36, "Unknown 36");
-        _tagNameMap.put(TAG_UNKNOWN_37, "Unknown 37");
-        _tagNameMap.put(TAG_UNKNOWN_38, "Unknown 38");
-        _tagNameMap.put(TAG_UNKNOWN_39, "Unknown 39");
-        _tagNameMap.put(TAG_VIGNETTE_CONTROL, "Vignette Control");
-        _tagNameMap.put(TAG_UNKNOWN_40, "Unknown 40");
-        _tagNameMap.put(TAG_UNKNOWN_41, "Unknown 41");
-        _tagNameMap.put(TAG_UNKNOWN_42, "Unknown 42");
-        _tagNameMap.put(TAG_UNKNOWN_43, "Unknown 43");
-        _tagNameMap.put(TAG_UNKNOWN_44, "Unknown 44");
-        _tagNameMap.put(TAG_UNKNOWN_45, "Unknown 45");
-        _tagNameMap.put(TAG_UNKNOWN_46, "Unknown 46");
-        _tagNameMap.put(TAG_UNKNOWN_47, "Unknown 47");
-        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
-
-        _tagNameMap.put(TAG_CAMERA_SERIAL_NUMBER_2, "Camera Serial Number");
-        _tagNameMap.put(TAG_IMAGE_DATA_SIZE, "Image Data Size");
-        _tagNameMap.put(TAG_UNKNOWN_27, "Unknown 27");
-        _tagNameMap.put(TAG_UNKNOWN_28, "Unknown 28");
-        _tagNameMap.put(TAG_IMAGE_COUNT, "Image Count");
-        _tagNameMap.put(TAG_DELETED_IMAGE_COUNT, "Deleted Image Count");
-        _tagNameMap.put(TAG_SATURATION_2, "Saturation");
-        _tagNameMap.put(TAG_DIGITAL_VARI_PROGRAM, "Digital Vari Program");
-        _tagNameMap.put(TAG_IMAGE_STABILISATION, "Image Stabilisation");
-        _tagNameMap.put(TAG_AF_RESPONSE, "AF Response");
-        _tagNameMap.put(TAG_UNKNOWN_29, "Unknown 29");
-        _tagNameMap.put(TAG_UNKNOWN_30, "Unknown 30");
-        _tagNameMap.put(TAG_MULTI_EXPOSURE, "Multi Exposure");
-        _tagNameMap.put(TAG_HIGH_ISO_NOISE_REDUCTION, "High ISO Noise Reduction");
-        _tagNameMap.put(TAG_UNKNOWN_31, "Unknown 31");
-        _tagNameMap.put(TAG_UNKNOWN_32, "Unknown 32");
-        _tagNameMap.put(TAG_UNKNOWN_33, "Unknown 33");
-        _tagNameMap.put(TAG_UNKNOWN_48, "Unknown 48");
-        _tagNameMap.put(TAG_POWER_UP_TIME, "Power Up Time");
-        _tagNameMap.put(TAG_AF_INFO_2, "AF Info 2");
-        _tagNameMap.put(TAG_FILE_INFO, "File Info");
-        _tagNameMap.put(TAG_AF_TUNE, "AF Tune");
-        _tagNameMap.put(TAG_FLASH_INFO, "Flash Info");
-        _tagNameMap.put(TAG_IMAGE_OPTIMISATION, "Image Optimisation");
-
-        _tagNameMap.put(TAG_IMAGE_ADJUSTMENT, "Image Adjustment");
-        _tagNameMap.put(TAG_CAMERA_TONE_COMPENSATION, "Tone Compensation");
-        _tagNameMap.put(TAG_ADAPTER, "Adapter");
-        _tagNameMap.put(TAG_LENS, "Lens");
-        _tagNameMap.put(TAG_MANUAL_FOCUS_DISTANCE, "Manual Focus Distance");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_CAMERA_COLOR_MODE, "Colour Mode");
-        _tagNameMap.put(TAG_CAMERA_HUE_ADJUSTMENT, "Camera Hue Adjustment");
-        _tagNameMap.put(TAG_NEF_COMPRESSION, "NEF Compression");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction");
-        _tagNameMap.put(TAG_LINEARIZATION_TABLE, "Linearization Table");
-        _tagNameMap.put(TAG_NIKON_CAPTURE_DATA, "Nikon Capture Data");
-        _tagNameMap.put(TAG_UNKNOWN_49, "Unknown 49");
-        _tagNameMap.put(TAG_UNKNOWN_50, "Unknown 50");
-        _tagNameMap.put(TAG_UNKNOWN_51, "Unknown 51");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print IM");
-        _tagNameMap.put(TAG_UNKNOWN_52, "Unknown 52");
-        _tagNameMap.put(TAG_UNKNOWN_53, "Unknown 53");
-        _tagNameMap.put(TAG_NIKON_CAPTURE_VERSION, "Nikon Capture Version");
-        _tagNameMap.put(TAG_NIKON_CAPTURE_OFFSETS, "Nikon Capture Offsets");
-        _tagNameMap.put(TAG_NIKON_SCAN, "Nikon Scan");
-        _tagNameMap.put(TAG_UNKNOWN_54, "Unknown 54");
-        _tagNameMap.put(TAG_NEF_BIT_DEPTH, "NEF Bit Depth");
-        _tagNameMap.put(TAG_UNKNOWN_55, "Unknown 55");
-    }
-
-    public NikonType2MakernoteDirectory()
-    {
-        this.setDescriptor(new NikonType2MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Nikon Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-	/** Nikon decryption tables used in exiftool */
-    private static final int[] _decTable1 =   {0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d,
-                                               0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d,
-                                               0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f,
-                                               0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f,
-                                               0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1,
-                                               0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17,
-                                               0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89,
-                                               0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f,
-                                               0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b,
-                                               0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb,
-                                               0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3,
-                                               0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f,
-                                               0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35,
-                                               0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43,
-                                               0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5,
-                                               0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 };
-    private static final int[] _decTable2 = { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c,
-                                               0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34,
-                                               0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad,
-                                               0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05,
-                                               0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee,
-                                               0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d,
-                                               0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b,
-                                               0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b,
-                                               0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc,
-                                               0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33,
-                                               0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8,
-                                               0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6,
-                                               0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c,
-                                               0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49,
-                                               0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb,
-                                               0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f };
-
-
-    /** decryption algorithm adapted from exiftool */
-    @Nullable
-    public int[] getDecryptedIntArray(int tagType)
-    {
-        int[] data = getIntArray(tagType);
-        Integer serial = getInteger(TAG_CAMERA_SERIAL_NUMBER);
-        Integer count = getInteger(TAG_EXPOSURE_SEQUENCE_NUMBER);
-
-        if (data == null || serial == null || count == null)
-            return null;
-
-        int key = 0;
-        for (int i = 0; i < 4; i++)
-            key ^= (count >> (i * 8)) & 0xff;
-
-        int ci = _decTable1[serial & 0xff];
-        int cj = _decTable2[key];
-        int ck = 0x60;
-
-        for (int i = 4; i < data.length; i++)
-        {
-            cj = (cj + ci * ck) & 0xff;
-            ck = (ck + 1) & 0xff;
-            data[i] ^= cj;
-        }
-
-        return data;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,1412 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DecimalFormat;
-import java.util.HashMap;
-
-import static com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusCameraSettingsMakernoteDirectory}.
- * <p>
- * Some Description functions and the Extender and Lens types lists converted from Exiftool version 10.10 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusCameraSettingsMakernoteDescriptor extends TagDescriptor<OlympusCameraSettingsMakernoteDirectory>
-{
-    public OlympusCameraSettingsMakernoteDescriptor(@NotNull OlympusCameraSettingsMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagCameraSettingsVersion:
-                return getCameraSettingsVersionDescription();
-            case TagPreviewImageValid:
-                return getPreviewImageValidDescription();
-
-            case TagExposureMode:
-                return getExposureModeDescription();
-            case TagAeLock:
-                return getAeLockDescription();
-            case TagMeteringMode:
-                return getMeteringModeDescription();
-            case TagExposureShift:
-                return getExposureShiftDescription();
-            case TagNdFilter:
-                return getNdFilterDescription();
-
-            case TagMacroMode:
-                return getMacroModeDescription();
-            case TagFocusMode:
-                return getFocusModeDescription();
-            case TagFocusProcess:
-                return getFocusProcessDescription();
-            case TagAfSearch:
-                return getAfSearchDescription();
-            case TagAfAreas:
-                return getAfAreasDescription();
-            case TagAfPointSelected:
-                return getAfPointSelectedDescription();
-            case TagAfFineTune:
-                return getAfFineTuneDescription();
-
-            case TagFlashMode:
-                return getFlashModeDescription();
-            case TagFlashRemoteControl:
-                return getFlashRemoteControlDescription();
-            case TagFlashControlMode:
-                return getFlashControlModeDescription();
-            case TagFlashIntensity:
-                return getFlashIntensityDescription();
-            case TagManualFlashStrength:
-                return getManualFlashStrengthDescription();
-
-            case TagWhiteBalance2:
-                return getWhiteBalance2Description();
-            case TagWhiteBalanceTemperature:
-                return getWhiteBalanceTemperatureDescription();
-            case TagCustomSaturation:
-                return getCustomSaturationDescription();
-            case TagModifiedSaturation:
-                return getModifiedSaturationDescription();
-            case TagContrastSetting:
-                return getContrastSettingDescription();
-            case TagSharpnessSetting:
-                return getSharpnessSettingDescription();
-            case TagColorSpace:
-                return getColorSpaceDescription();
-            case TagSceneMode:
-                return getSceneModeDescription();
-            case TagNoiseReduction:
-                return getNoiseReductionDescription();
-            case TagDistortionCorrection:
-                return getDistortionCorrectionDescription();
-            case TagShadingCompensation:
-                return getShadingCompensationDescription();
-            case TagGradation:
-                return getGradationDescription();
-            case TagPictureMode:
-                return getPictureModeDescription();
-            case TagPictureModeSaturation:
-                return getPictureModeSaturationDescription();
-            case TagPictureModeContrast:
-                return getPictureModeContrastDescription();
-            case TagPictureModeSharpness:
-                return getPictureModeSharpnessDescription();
-            case TagPictureModeBWFilter:
-                return getPictureModeBWFilterDescription();
-            case TagPictureModeTone:
-                return getPictureModeToneDescription();
-            case TagNoiseFilter:
-                return getNoiseFilterDescription();
-            case TagArtFilter:
-                return getArtFilterDescription();
-            case TagMagicFilter:
-                return getMagicFilterDescription();
-            case TagPictureModeEffect:
-                return getPictureModeEffectDescription();
-            case TagToneLevel:
-                return getToneLevelDescription();
-            case TagArtFilterEffect:
-                return getArtFilterEffectDescription();
-            case TagColorCreatorEffect:
-                return getColorCreatorEffectDescription();
-
-            case TagDriveMode:
-                return getDriveModeDescription();
-            case TagPanoramaMode:
-                return getPanoramaModeDescription();
-            case TagImageQuality2:
-                return getImageQuality2Description();
-            case TagImageStabilization:
-                return getImageStabilizationDescription();
-
-            case TagStackedImage:
-                return getStackedImageDescription();
-
-            case TagManometerPressure:
-                return getManometerPressureDescription();
-            case TagManometerReading:
-                return getManometerReadingDescription();
-            case TagExtendedWBDetect:
-                return getExtendedWBDetectDescription();
-            case TagRollAngle:
-                return getRollAngleDescription();
-            case TagPitchAngle:
-                return getPitchAngleDescription();
-            case TagDateTimeUtc:
-                return getDateTimeUTCDescription();
-
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getCameraSettingsVersionDescription()
-    {
-        return getVersionBytesDescription(TagCameraSettingsVersion, 4);
-    }
-
-    @Nullable
-    public String getPreviewImageValidDescription()
-    {
-        return getIndexedDescription(TagPreviewImageValid,
-            "No", "Yes");
-    }
-
-    @Nullable
-    public String getExposureModeDescription()
-    {
-        return getIndexedDescription(TagExposureMode, 1,
-            "Manual", "Program", "Aperture-priority AE", "Shutter speed priority", "Program-shift");
-    }
-
-    @Nullable
-    public String getAeLockDescription()
-    {
-        return getIndexedDescription(TagAeLock,
-            "Off", "On");
-    }
-
-    @Nullable
-    public String getMeteringModeDescription()
-    {
-        Integer value = _directory.getInteger(TagMeteringMode);
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 2:
-                return "Center-weighted average";
-            case 3:
-                return "Spot";
-            case 5:
-                return "ESP";
-            case 261:
-                return "Pattern+AF";
-            case 515:
-                return "Spot+Highlight control";
-            case 1027:
-                return "Spot+Shadow control";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getExposureShiftDescription()
-    {
-        return getRationalOrDoubleString(TagExposureShift);
-    }
-
-    @Nullable
-    public String getNdFilterDescription()
-    {
-        return getIndexedDescription(TagNdFilter, "Off", "On");
-    }
-
-    @Nullable
-    public String getMacroModeDescription()
-    {
-        return getIndexedDescription(TagMacroMode, "Off", "On", "Super Macro");
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        int[] values = _directory.getIntArray(TagFocusMode);
-        if (values == null) {
-            // check if it's only one value long also
-            Integer value = _directory.getInteger(TagFocusMode);
-            if (value == null)
-                return null;
-
-            values = new int[]{value};
-        }
-
-        if (values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        switch (values[0]) {
-            case 0:
-                sb.append("Single AF");
-                break;
-            case 1:
-                sb.append("Sequential shooting AF");
-                break;
-            case 2:
-                sb.append("Continuous AF");
-                break;
-            case 3:
-                sb.append("Multi AF");
-                break;
-            case 4:
-                sb.append("Face detect");
-                break;
-            case 10:
-                sb.append("MF");
-                break;
-            default:
-                sb.append("Unknown (" + values[0] + ")");
-                break;
-        }
-
-        if (values.length > 1) {
-            sb.append("; ");
-            int value1 = values[1];
-
-            if (value1 == 0) {
-                sb.append("(none)");
-            } else {
-                if (( value1       & 1) > 0) sb.append("S-AF, ");
-                if (((value1 >> 2) & 1) > 0) sb.append("C-AF, ");
-                if (((value1 >> 4) & 1) > 0) sb.append("MF, ");
-                if (((value1 >> 5) & 1) > 0) sb.append("Face detect, ");
-                if (((value1 >> 6) & 1) > 0) sb.append("Imager AF, ");
-                if (((value1 >> 7) & 1) > 0) sb.append("Live View Magnification Frame, ");
-                if (((value1 >> 8) & 1) > 0) sb.append("AF sensor, ");
-
-                sb.setLength(sb.length() - 2);
-            }
-        }
-
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getFocusProcessDescription()
-    {
-        int[] values = _directory.getIntArray(TagFocusProcess);
-        if (values == null) {
-            // check if it's only one value long also
-            Integer value = _directory.getInteger(TagFocusProcess);
-            if (value == null)
-                return null;
-
-            values = new int[]{value};
-        }
-
-        if (values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-
-        switch (values[0]) {
-            case 0:
-                sb.append("AF not used");
-                break;
-            case 1:
-                sb.append("AF used");
-                break;
-            default:
-                sb.append("Unknown (" + values[0] + ")");
-                break;
-        }
-
-        if (values.length > 1)
-            sb.append("; " + values[1]);
-
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getAfSearchDescription()
-    {
-        return getIndexedDescription(TagAfSearch, "Not Ready", "Ready");
-    }
-
-    /// <summary>
-    /// coordinates range from 0 to 255
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getAfAreasDescription()
-    {
-        Object obj = _directory.getObject(TagAfAreas);
-        if (obj == null || !(obj instanceof long[]))
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (long point : (long[]) obj) {
-            if (point == 0L)
-                continue;
-            if (sb.length() != 0)
-                sb.append(", ");
-
-            if (point == 0x36794285L)
-                sb.append("Left ");
-            else if (point == 0x79798585L)
-                sb.append("Center ");
-            else if (point == 0xBD79C985L)
-                sb.append("Right ");
-
-            sb.append(String.format("(%d/255,%d/255)-(%d/255,%d/255)",
-                (point >> 24) & 0xFF,
-                (point >> 16) & 0xFF,
-                (point >> 8) & 0xFF,
-                point & 0xFF));
-        }
-
-        return sb.length() == 0 ? null : sb.toString();
-    }
-
-    /// <summary>
-    /// coordinates expressed as a percent
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getAfPointSelectedDescription()
-    {
-        Rational[] values = _directory.getRationalArray(TagAfPointSelected);
-        if (values == null)
-            return "n/a";
-
-        if (values.length < 4)
-            return null;
-
-        int index = 0;
-        if (values.length == 5 && values[0].longValue() == 0)
-            index = 1;
-
-        int p1 = (int)(values[index].doubleValue() * 100);
-        int p2 = (int)(values[index + 1].doubleValue() * 100);
-        int p3 = (int)(values[index + 2].doubleValue() * 100);
-        int p4 = (int)(values[index + 3].doubleValue() * 100);
-
-        if(p1 + p2 + p3 + p4 == 0)
-            return "n/a";
-
-        return String.format("(%d%%,%d%%) (%d%%,%d%%)", p1, p2, p3, p4);
-    }
-
-    @Nullable
-    public String getAfFineTuneDescription()
-    {
-        return getIndexedDescription(TagAfFineTune, "Off", "On");
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        Integer value = _directory.getInteger(TagFlashMode);
-        if (value == null)
-            return null;
-
-        if (value == 0)
-            return "Off";
-
-        StringBuilder sb = new StringBuilder();
-        int v = value;
-
-        if (( v       & 1) != 0) sb.append("On, ");
-        if (((v >> 1) & 1) != 0) sb.append("Fill-in, ");
-        if (((v >> 2) & 1) != 0) sb.append("Red-eye, ");
-        if (((v >> 3) & 1) != 0) sb.append("Slow-sync, ");
-        if (((v >> 4) & 1) != 0) sb.append("Forced On, ");
-        if (((v >> 5) & 1) != 0) sb.append("2nd Curtain, ");
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getFlashRemoteControlDescription()
-    {
-        Integer value = _directory.getInteger(TagFlashRemoteControl);
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0:
-                return "Off";
-            case 0x01:
-                return "Channel 1, Low";
-            case 0x02:
-                return "Channel 2, Low";
-            case 0x03:
-                return "Channel 3, Low";
-            case 0x04:
-                return "Channel 4, Low";
-            case 0x09:
-                return "Channel 1, Mid";
-            case 0x0a:
-                return "Channel 2, Mid";
-            case 0x0b:
-                return "Channel 3, Mid";
-            case 0x0c:
-                return "Channel 4, Mid";
-            case 0x11:
-                return "Channel 1, High";
-            case 0x12:
-                return "Channel 2, High";
-            case 0x13:
-                return "Channel 3, High";
-            case 0x14:
-                return "Channel 4, High";
-
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    /// <summary>
-    /// 3 or 4 values
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getFlashControlModeDescription()
-    {
-        int[] values = _directory.getIntArray(TagFlashControlMode);
-        if (values == null)
-            return null;
-
-        if (values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-
-        switch (values[0]) {
-            case 0:
-                sb.append("Off");
-                break;
-            case 3:
-                sb.append("TTL");
-                break;
-            case 4:
-                sb.append("Auto");
-                break;
-            case 5:
-                sb.append("Manual");
-                break;
-            default:
-                sb.append("Unknown (").append(values[0]).append(")");
-                break;
-        }
-
-        for (int i = 1; i < values.length; i++)
-            sb.append("; ").append(values[i]);
-
-        return sb.toString();
-    }
-
-    /// <summary>
-    /// 3 or 4 values
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getFlashIntensityDescription()
-    {
-        Rational[] values = _directory.getRationalArray(TagFlashIntensity);
-        if (values == null || values.length == 0)
-            return null;
-
-        if (values.length == 3) {
-            if (values[0].getDenominator() == 0 && values[1].getDenominator() == 0 && values[2].getDenominator() == 0)
-                return "n/a";
-        } else if (values.length == 4) {
-            if (values[0].getDenominator() == 0 && values[1].getDenominator() == 0 && values[2].getDenominator() == 0 && values[3].getDenominator() == 0)
-                return "n/a (x4)";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        for (Rational t : values)
-            sb.append(t).append(", ");
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getManualFlashStrengthDescription()
-    {
-        Rational[] values = _directory.getRationalArray(TagManualFlashStrength);
-        if (values == null || values.length == 0)
-            return "n/a";
-
-        if (values.length == 3) {
-            if (values[0].getDenominator() == 0 && values[1].getDenominator() == 0 && values[2].getDenominator() == 0)
-                return "n/a";
-        } else if (values.length == 4) {
-            if (values[0].getDenominator() == 0 && values[1].getDenominator() == 0 && values[2].getDenominator() == 0 && values[3].getDenominator() == 0)
-                return "n/a (x4)";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        for (Rational t : values)
-            sb.append(t).append(", ");
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getWhiteBalance2Description()
-    {
-        Integer value = _directory.getInteger(TagWhiteBalance2);
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0:
-                return "Auto";
-            case 1:
-                return "Auto (Keep Warm Color Off)";
-            case 16:
-                return "7500K (Fine Weather with Shade)";
-            case 17:
-                return "6000K (Cloudy)";
-            case 18:
-                return "5300K (Fine Weather)";
-            case 20:
-                return "3000K (Tungsten light)";
-            case 21:
-                return "3600K (Tungsten light-like)";
-            case 22:
-                return "Auto Setup";
-            case 23:
-                return "5500K (Flash)";
-            case 33:
-                return "6600K (Daylight fluorescent)";
-            case 34:
-                return "4500K (Neutral white fluorescent)";
-            case 35:
-                return "4000K (Cool white fluorescent)";
-            case 36:
-                return "White Fluorescent";
-            case 48:
-                return "3600K (Tungsten light-like)";
-            case 67:
-                return "Underwater";
-            case 256:
-                return "One Touch WB 1";
-            case 257:
-                return "One Touch WB 2";
-            case 258:
-                return "One Touch WB 3";
-            case 259:
-                return "One Touch WB 4";
-            case 512:
-                return "Custom WB 1";
-            case 513:
-                return "Custom WB 2";
-            case 514:
-                return "Custom WB 3";
-            case 515:
-                return "Custom WB 4";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceTemperatureDescription()
-    {
-        Integer value = _directory.getInteger(TagWhiteBalanceTemperature);
-        if (value == null)
-            return null;
-        if (value == 0)
-            return "Auto";
-        return value.toString();
-    }
-
-    @Nullable
-    public String getCustomSaturationDescription()
-    {
-        // TODO: if model is /^E-1\b/  then
-        // $a-=$b; $c-=$b;
-        // return "CS$a (min CS0, max CS$c)"
-        return getValueMinMaxDescription(TagCustomSaturation);
-    }
-
-    @Nullable
-    public String getModifiedSaturationDescription()
-    {
-        return getIndexedDescription(TagModifiedSaturation,
-            "Off", "CM1 (Red Enhance)", "CM2 (Green Enhance)", "CM3 (Blue Enhance)", "CM4 (Skin Tones)");
-    }
-
-    @Nullable
-    public String getContrastSettingDescription()
-    {
-        return getValueMinMaxDescription(TagContrastSetting);
-    }
-
-    @Nullable
-    public String getSharpnessSettingDescription()
-    {
-        return getValueMinMaxDescription(TagSharpnessSetting);
-    }
-
-    @Nullable
-    public String getColorSpaceDescription()
-    {
-        return getIndexedDescription(TagColorSpace,
-            "sRGB", "Adobe RGB", "Pro Photo RGB");
-    }
-
-    @Nullable
-    public String getSceneModeDescription()
-    {
-        Integer value = _directory.getInteger(TagSceneMode);
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0:
-                return "Standard";
-            case 6:
-                return "Auto";
-            case 7:
-                return "Sport";
-            case 8:
-                return "Portrait";
-            case 9:
-                return "Landscape+Portrait";
-            case 10:
-                return "Landscape";
-            case 11:
-                return "Night Scene";
-            case 12:
-                return "Self Portrait";
-            case 13:
-                return "Panorama";
-            case 14:
-                return "2 in 1";
-            case 15:
-                return "Movie";
-            case 16:
-                return "Landscape+Portrait";
-            case 17:
-                return "Night+Portrait";
-            case 18:
-                return "Indoor";
-            case 19:
-                return "Fireworks";
-            case 20:
-                return "Sunset";
-            case 21:
-                return "Beauty Skin";
-            case 22:
-                return "Macro";
-            case 23:
-                return "Super Macro";
-            case 24:
-                return "Food";
-            case 25:
-                return "Documents";
-            case 26:
-                return "Museum";
-            case 27:
-                return "Shoot & Select";
-            case 28:
-                return "Beach & Snow";
-            case 29:
-                return "Self Portrait+Timer";
-            case 30:
-                return "Candle";
-            case 31:
-                return "Available Light";
-            case 32:
-                return "Behind Glass";
-            case 33:
-                return "My Mode";
-            case 34:
-                return "Pet";
-            case 35:
-                return "Underwater Wide1";
-            case 36:
-                return "Underwater Macro";
-            case 37:
-                return "Shoot & Select1";
-            case 38:
-                return "Shoot & Select2";
-            case 39:
-                return "High Key";
-            case 40:
-                return "Digital Image Stabilization";
-            case 41:
-                return "Auction";
-            case 42:
-                return "Beach";
-            case 43:
-                return "Snow";
-            case 44:
-                return "Underwater Wide2";
-            case 45:
-                return "Low Key";
-            case 46:
-                return "Children";
-            case 47:
-                return "Vivid";
-            case 48:
-                return "Nature Macro";
-            case 49:
-                return "Underwater Snapshot";
-            case 50:
-                return "Shooting Guide";
-            case 54:
-                return "Face Portrait";
-            case 57:
-                return "Bulb";
-            case 59:
-                return "Smile Shot";
-            case 60:
-                return "Quick Shutter";
-            case 63:
-                return "Slow Shutter";
-            case 64:
-                return "Bird Watching";
-            case 65:
-                return "Multiple Exposure";
-            case 66:
-                return "e-Portrait";
-            case 67:
-                return "Soft Background Shot";
-            case 142:
-                return "Hand-held Starlight";
-            case 154:
-                return "HDR";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getNoiseReductionDescription()
-    {
-        Integer value = _directory.getInteger(TagNoiseReduction);
-        if (value == null)
-            return null;
-
-        if (value == 0)
-            return "(none)";
-
-        StringBuilder sb = new StringBuilder();
-        int v = value;
-
-        if ((v & 1) != 0) sb.append("Noise Reduction, ");
-        if (((v >> 1) & 1) != 0) sb.append("Noise Filter, ");
-        if (((v >> 2) & 1) != 0) sb.append("Noise Filter (ISO Boost), ");
-        if (((v >> 3) & 1) != 0) sb.append("Auto, ");
-
-        return sb.length() != 0
-            ? sb.substring(0, sb.length() - 2)
-            : "(none)";
-    }
-
-    @Nullable
-    public String getDistortionCorrectionDescription()
-    {
-        return getIndexedDescription(TagDistortionCorrection, "Off", "On");
-    }
-
-    @Nullable
-    public String getShadingCompensationDescription()
-    {
-        return getIndexedDescription(TagShadingCompensation, "Off", "On");
-    }
-
-    /// <summary>
-    /// 3 or 4 values
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getGradationDescription()
-    {
-        int[] values = _directory.getIntArray(TagGradation);
-        if (values == null || values.length < 3)
-            return null;
-
-        String join = String.format("%d %d %d", values[0], values[1], values[2]);
-
-        String ret;
-        if (join.equals("0 0 0")) {
-            ret = "n/a";
-        } else if (join.equals("-1 -1 1")) {
-            ret = "Low Key";
-        } else if (join.equals("0 -1 1")) {
-            ret = "Normal";
-        } else if (join.equals("1 -1 1")) {
-            ret = "High Key";
-        } else {
-            ret = "Unknown (" + join + ")";
-        }
-
-        if (values.length > 3) {
-            if (values[3] == 0)
-                ret += "; User-Selected";
-            else if (values[3] == 1)
-                ret += "; Auto-Override";
-        }
-
-        return ret;
-    }
-
-    /// <summary>
-    /// 1 or 2 values
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getPictureModeDescription()
-    {
-        int[] values = _directory.getIntArray(TagPictureMode);
-        if (values == null) {
-            // check if it's only one value long also
-            Integer value = _directory.getInteger(TagNoiseReduction);
-            if (value == null)
-                return null;
-
-            values = new int[]{value};
-        }
-
-        if (values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        switch (values[0]) {
-            case 1:
-                sb.append("Vivid");
-                break;
-            case 2:
-                sb.append("Natural");
-                break;
-            case 3:
-                sb.append("Muted");
-                break;
-            case 4:
-                sb.append("Portrait");
-                break;
-            case 5:
-                sb.append("i-Enhance");
-                break;
-            case 256:
-                sb.append("Monotone");
-                break;
-            case 512:
-                sb.append("Sepia");
-                break;
-            default:
-                sb.append("Unknown (").append(values[0]).append(")");
-                break;
-        }
-
-        if (values.length > 1)
-            sb.append("; ").append(values[1]);
-
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getPictureModeSaturationDescription()
-    {
-        return getValueMinMaxDescription(TagPictureModeSaturation);
-    }
-
-    @Nullable
-    public String getPictureModeContrastDescription()
-    {
-        return getValueMinMaxDescription(TagPictureModeContrast);
-    }
-
-    @Nullable
-    public String getPictureModeSharpnessDescription()
-    {
-        return getValueMinMaxDescription(TagPictureModeSharpness);
-    }
-
-    @Nullable
-    public String getPictureModeBWFilterDescription()
-    {
-        return getIndexedDescription(TagPictureModeBWFilter,
-            "n/a", "Neutral", "Yellow", "Orange", "Red", "Green");
-    }
-
-    @Nullable
-    public String getPictureModeToneDescription()
-    {
-        return getIndexedDescription(TagPictureModeTone,
-            "n/a", "Neutral", "Sepia", "Blue", "Purple", "Green");
-    }
-
-    @Nullable
-    public String getNoiseFilterDescription()
-    {
-        int[] values = _directory.getIntArray(TagNoiseFilter);
-        if (values == null)
-            return null;
-
-        String join = String.format("%d %d %d", values[0], values[1], values[2]);
-
-        if (join.equals("0 0 0"))
-            return "n/a";
-        if (join.equals("-2 -2 1"))
-            return "Off";
-        if (join.equals("-1 -2 1"))
-            return "Low";
-        if (join.equals("0 -2 1"))
-            return "Standard";
-        if (join.equals("1 -2 1"))
-            return "High";
-        return "Unknown (" + join + ")";
-    }
-
-    @Nullable
-    public String getArtFilterDescription()
-    {
-        return getFiltersDescription(TagArtFilter);
-    }
-
-    @Nullable
-    public String getMagicFilterDescription()
-    {
-        return getFiltersDescription(TagMagicFilter);
-    }
-
-    @Nullable
-    public String getPictureModeEffectDescription()
-    {
-        int[] values = _directory.getIntArray(TagPictureModeEffect);
-        if (values == null)
-            return null;
-
-        String key = String.format("%d %d %d", values[0], values[1], values[2]);
-        if (key.equals("0 0 0"))
-            return "n/a";
-        if (key.equals("-1 -1 1"))
-            return "Low";
-        if (key.equals("0 -1 1"))
-            return "Standard";
-        if (key.equals("1 -1 1"))
-            return "High";
-        return "Unknown (" + key + ")";
-    }
-
-    @Nullable
-    public String getToneLevelDescription()
-    {
-        int[] values = _directory.getIntArray(TagToneLevel);
-        if (values == null || values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if (i == 0 || i == 4 || i == 8 || i == 12 || i == 16 || i == 20 || i == 24)
-                sb.append(_toneLevelType.get(values[i])).append("; ");
-            else
-                sb.append(values[i]).append("; ");
-        }
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getArtFilterEffectDescription()
-    {
-        int[] values = _directory.getIntArray(TagArtFilterEffect);
-        if (values == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if (i == 0) {
-                sb.append((_filters.containsKey(values[i]) ? _filters.get(values[i]) : "[unknown]")).append("; ");
-            } else if (i == 3) {
-                sb.append("Partial Color ").append(values[i]).append("; ");
-            } else if (i == 4) {
-                switch (values[i]) {
-                    case 0x0000:
-                        sb.append("No Effect");
-                        break;
-                    case 0x8010:
-                        sb.append("Star Light");
-                        break;
-                    case 0x8020:
-                        sb.append("Pin Hole");
-                        break;
-                    case 0x8030:
-                        sb.append("Frame");
-                        break;
-                    case 0x8040:
-                        sb.append("Soft Focus");
-                        break;
-                    case 0x8050:
-                        sb.append("White Edge");
-                        break;
-                    case 0x8060:
-                        sb.append("B&W");
-                        break;
-                    default:
-                        sb.append("Unknown (").append(values[i]).append(")");
-                        break;
-                }
-                sb.append("; ");
-            } else if (i == 6) {
-                switch (values[i]) {
-                    case 0:
-                        sb.append("No Color Filter");
-                        break;
-                    case 1:
-                        sb.append("Yellow Color Filter");
-                        break;
-                    case 2:
-                        sb.append("Orange Color Filter");
-                        break;
-                    case 3:
-                        sb.append("Red Color Filter");
-                        break;
-                    case 4:
-                        sb.append("Green Color Filter");
-                        break;
-                    default:
-                        sb.append("Unknown (").append(values[i]).append(")");
-                        break;
-                }
-                sb.append("; ");
-            } else {
-                sb.append(values[i]).append("; ");
-            }
-        }
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getColorCreatorEffectDescription()
-    {
-        int[] values = _directory.getIntArray(TagColorCreatorEffect);
-        if (values == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if (i == 0) {
-                sb.append("Color ").append(values[i]).append("; ");
-            } else if (i == 3) {
-                sb.append("Strength ").append(values[i]).append("; ");
-            } else {
-                sb.append(values[i]).append("; ");
-            }
-        }
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    /// <summary>
-    /// 2 or 3 numbers: 1. Mode, 2. Shot number, 3. Mode bits
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getDriveModeDescription()
-    {
-        int[] values = _directory.getIntArray(TagDriveMode);
-        if (values == null)
-            return null;
-
-        if (values.length == 0 || values[0] == 0)
-            return "Single Shot";
-
-        StringBuilder a = new StringBuilder();
-
-        if (values[0] == 5 && values.length >= 3) {
-            int c = values[2];
-            if (( c       & 1) > 0) a.append("AE");
-            if (((c >> 1) & 1) > 0) a.append("WB");
-            if (((c >> 2) & 1) > 0) a.append("FL");
-            if (((c >> 3) & 1) > 0) a.append("MF");
-            if (((c >> 6) & 1) > 0) a.append("Focus");
-
-            a.append(" Bracketing");
-        } else {
-            switch (values[0]) {
-                case 1:
-                    a.append("Continuous Shooting");
-                    break;
-                case 2:
-                    a.append("Exposure Bracketing");
-                    break;
-                case 3:
-                    a.append("White Balance Bracketing");
-                    break;
-                case 4:
-                    a.append("Exposure+WB Bracketing");
-                    break;
-                default:
-                    a.append("Unknown (").append(values[0]).append(")");
-                    break;
-            }
-        }
-
-        a.append(", Shot ").append(values[1]);
-
-        return a.toString();
-    }
-
-    /// <summary>
-    /// 2 numbers: 1. Mode, 2. Shot number
-    /// </summary>
-    /// <returns></returns>
-    @Nullable
-    public String getPanoramaModeDescription()
-    {
-        int[] values = _directory.getIntArray(TagPanoramaMode);
-        if (values == null)
-            return null;
-
-        if (values.length == 0 || values[0] == 0)
-            return "Off";
-
-        String a;
-        switch (values[0]) {
-            case 1:
-                a = "Left to Right";
-                break;
-            case 2:
-                a = "Right to Left";
-                break;
-            case 3:
-                a = "Bottom to Top";
-                break;
-            case 4:
-                a = "Top to Bottom";
-                break;
-            default:
-                a = "Unknown (" + values[0] + ")";
-                break;
-        }
-
-        return String.format("%s, Shot %d", a, values[1]);
-    }
-
-    @Nullable
-    public String getImageQuality2Description()
-    {
-        return getIndexedDescription(TagImageQuality2, 1,
-            "SQ", "HQ", "SHQ", "RAW", "SQ (5)");
-    }
-
-    @Nullable
-    public String getImageStabilizationDescription()
-    {
-        return getIndexedDescription(TagImageStabilization,
-            "Off", "On, Mode 1", "On, Mode 2", "On, Mode 3", "On, Mode 4");
-    }
-
-    @Nullable
-    public String getStackedImageDescription()
-    {
-        int[] values = _directory.getIntArray(TagStackedImage);
-        if (values == null || values.length < 2)
-            return null;
-
-        int v1 = values[0];
-        int v2 = values[1];
-
-        if (v1 == 0 && v2 == 0)
-            return "No";
-        if (v1 == 9 && v2 == 8)
-            return "Focus-stacked (8 images)";
-
-        return String.format("Unknown (%d %d)", v1, v2);
-    }
-
-    /// <remarks>
-    /// TODO: need better image examples to test this function
-    /// </remarks>
-    /// <returns></returns>
-    @Nullable
-    public String getManometerPressureDescription()
-    {
-        Integer value = _directory.getInteger(TagManometerPressure);
-        if (value == null)
-            return null;
-
-        return String.format("%s kPa", new DecimalFormat("#.##").format(value / 10.0));
-    }
-
-    /// <remarks>
-    /// TODO: need better image examples to test this function
-    /// </remarks>
-    /// <returns></returns>
-    @Nullable
-    public String getManometerReadingDescription()
-    {
-        int[] values = _directory.getIntArray(TagManometerReading);
-        if (values == null || values.length < 2)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("#.##");
-        return String.format("%s m, %s ft",
-            format.format(values[0] / 10.0),
-            format.format(values[1] / 10.0));
-    }
-
-    @Nullable
-    public String getExtendedWBDetectDescription()
-    {
-        return getIndexedDescription(TagExtendedWBDetect, "Off", "On");
-    }
-
-    /// <summary>
-    /// converted to degrees of clockwise camera rotation
-    /// </summary>
-    /// <remarks>
-    /// TODO: need better image examples to test this function
-    /// </remarks>
-    /// <returns></returns>
-    @Nullable
-    public String getRollAngleDescription()
-    {
-        int[] values = _directory.getIntArray(TagRollAngle);
-        if (values == null || values.length < 2)
-            return null;
-
-        String ret = values[0] != 0
-            ? Double.toString(-values[0] / 10.0)
-            : "n/a";
-
-        return String.format("%s %d", ret, values[1]);
-    }
-
-    /// <summary>
-    /// converted to degrees of upward camera tilt
-    /// </summary>
-    /// <remarks>
-    /// TODO: need better image examples to test this function
-    /// </remarks>
-    /// <returns></returns>
-    @Nullable
-    public String getPitchAngleDescription()
-    {
-        int[] values = _directory.getIntArray(TagPitchAngle);
-        if (values == null || values.length < 2)
-            return null;
-
-        // (second value is 0 if level gauge is off)
-        String ret = values[0] != 0
-            ? Double.toString(values[0] / 10.0)
-            : "n/a";
-
-        return String.format("%s %d", ret, values[1]);
-    }
-
-    @Nullable
-    public String getDateTimeUTCDescription()
-    {
-        Object value = _directory.getObject(TagDateTimeUtc);
-        if (value == null)
-            return null;
-        return value.toString();
-    }
-
-    @Nullable
-    private String getValueMinMaxDescription(int tagId)
-    {
-        int[] values = _directory.getIntArray(tagId);
-        if (values == null || values.length < 3)
-            return null;
-
-        return String.format("%d (min %d, max %d)", values[0], values[1], values[2]);
-    }
-
-    @Nullable
-    private String getFiltersDescription(int tagId)
-    {
-        int[] values = _directory.getIntArray(tagId);
-        if (values == null || values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if (i == 0)
-                sb.append(_filters.containsKey(values[i]) ? _filters.get(values[i]) : "[unknown]");
-            else
-                sb.append(values[i]);
-            sb.append("; ");
-        }
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    private static final HashMap<Integer, String> _toneLevelType = new HashMap<Integer, String>();
-    // ArtFilter, ArtFilterEffect and MagicFilter values
-    private static final HashMap<Integer, String> _filters = new HashMap<Integer, String>();
-
-    static {
-        _filters.put(0, "Off");
-        _filters.put(1, "Soft Focus");
-        _filters.put(2, "Pop Art");
-        _filters.put(3, "Pale & Light Color");
-        _filters.put(4, "Light Tone");
-        _filters.put(5, "Pin Hole");
-        _filters.put(6, "Grainy Film");
-        _filters.put(9, "Diorama");
-        _filters.put(10, "Cross Process");
-        _filters.put(12, "Fish Eye");
-        _filters.put(13, "Drawing");
-        _filters.put(14, "Gentle Sepia");
-        _filters.put(15, "Pale & Light Color II");
-        _filters.put(16, "Pop Art II");
-        _filters.put(17, "Pin Hole II");
-        _filters.put(18, "Pin Hole III");
-        _filters.put(19, "Grainy Film II");
-        _filters.put(20, "Dramatic Tone");
-        _filters.put(21, "Punk");
-        _filters.put(22, "Soft Focus 2");
-        _filters.put(23, "Sparkle");
-        _filters.put(24, "Watercolor");
-        _filters.put(25, "Key Line");
-        _filters.put(26, "Key Line II");
-        _filters.put(27, "Miniature");
-        _filters.put(28, "Reflection");
-        _filters.put(29, "Fragmented");
-        _filters.put(31, "Cross Process II");
-        _filters.put(32, "Dramatic Tone II");
-        _filters.put(33, "Watercolor I");
-        _filters.put(34, "Watercolor II");
-        _filters.put(35, "Diorama II");
-        _filters.put(36, "Vintage");
-        _filters.put(37, "Vintage II");
-        _filters.put(38, "Vintage III");
-        _filters.put(39, "Partial Color");
-        _filters.put(40, "Partial Color II");
-        _filters.put(41, "Partial Color III");
-
-        _toneLevelType.put(0, "0");
-        _toneLevelType.put(-31999, "Highlights ");
-        _toneLevelType.put(-31998, "Shadows ");
-        _toneLevelType.put(-31997, "Midtones ");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusCameraSettingsMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,201 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * The Olympus camera settings makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusCameraSettingsMakernoteDirectory extends Directory
-{
-    public static final int TagCameraSettingsVersion = 0x0000;
-    public static final int TagPreviewImageValid = 0x0100;
-    public static final int TagPreviewImageStart = 0x0101;
-    public static final int TagPreviewImageLength = 0x0102;
-
-    public static final int TagExposureMode = 0x0200;
-    public static final int TagAeLock = 0x0201;
-    public static final int TagMeteringMode = 0x0202;
-    public static final int TagExposureShift = 0x0203;
-    public static final int TagNdFilter = 0x0204;
-
-    public static final int TagMacroMode = 0x0300;
-    public static final int TagFocusMode = 0x0301;
-    public static final int TagFocusProcess = 0x0302;
-    public static final int TagAfSearch = 0x0303;
-    public static final int TagAfAreas = 0x0304;
-    public static final int TagAfPointSelected = 0x0305;
-    public static final int TagAfFineTune = 0x0306;
-    public static final int TagAfFineTuneAdj = 0x0307;
-
-    public static final int TagFlashMode = 0x400;
-    public static final int TagFlashExposureComp = 0x401;
-    public static final int TagFlashRemoteControl = 0x403;
-    public static final int TagFlashControlMode = 0x404;
-    public static final int TagFlashIntensity = 0x405;
-    public static final int TagManualFlashStrength = 0x406;
-
-    public static final int TagWhiteBalance2 = 0x500;
-    public static final int TagWhiteBalanceTemperature = 0x501;
-    public static final int TagWhiteBalanceBracket = 0x502;
-    public static final int TagCustomSaturation = 0x503;
-    public static final int TagModifiedSaturation = 0x504;
-    public static final int TagContrastSetting = 0x505;
-    public static final int TagSharpnessSetting = 0x506;
-    public static final int TagColorSpace = 0x507;
-    public static final int TagSceneMode = 0x509;
-    public static final int TagNoiseReduction = 0x50a;
-    public static final int TagDistortionCorrection = 0x50b;
-    public static final int TagShadingCompensation = 0x50c;
-    public static final int TagCompressionFactor = 0x50d;
-    public static final int TagGradation = 0x50f;
-    public static final int TagPictureMode = 0x520;
-    public static final int TagPictureModeSaturation = 0x521;
-    public static final int TagPictureModeHue = 0x522;
-    public static final int TagPictureModeContrast = 0x523;
-    public static final int TagPictureModeSharpness = 0x524;
-    public static final int TagPictureModeBWFilter = 0x525;
-    public static final int TagPictureModeTone = 0x526;
-    public static final int TagNoiseFilter = 0x527;
-    public static final int TagArtFilter = 0x529;
-    public static final int TagMagicFilter = 0x52c;
-    public static final int TagPictureModeEffect = 0x52d;
-    public static final int TagToneLevel = 0x52e;
-    public static final int TagArtFilterEffect = 0x52f;
-    public static final int TagColorCreatorEffect = 0x532;
-
-    public static final int TagDriveMode = 0x600;
-    public static final int TagPanoramaMode = 0x601;
-    public static final int TagImageQuality2 = 0x603;
-    public static final int TagImageStabilization = 0x604;
-
-    public static final int TagStackedImage = 0x804;
-
-    public static final int TagManometerPressure = 0x900;
-    public static final int TagManometerReading = 0x901;
-    public static final int TagExtendedWBDetect = 0x902;
-    public static final int TagRollAngle = 0x903;
-    public static final int TagPitchAngle = 0x904;
-    public static final int TagDateTimeUtc = 0x908;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TagCameraSettingsVersion, "Camera Settings Version");
-        _tagNameMap.put(TagPreviewImageValid, "Preview Image Valid");
-        _tagNameMap.put(TagPreviewImageStart, "Preview Image Start");
-        _tagNameMap.put(TagPreviewImageLength, "Preview Image Length");
-
-        _tagNameMap.put(TagExposureMode, "Exposure Mode");
-        _tagNameMap.put(TagAeLock, "AE Lock");
-        _tagNameMap.put(TagMeteringMode, "Metering Mode");
-        _tagNameMap.put(TagExposureShift, "Exposure Shift");
-        _tagNameMap.put(TagNdFilter, "ND Filter");
-
-        _tagNameMap.put(TagMacroMode, "Macro Mode");
-        _tagNameMap.put(TagFocusMode, "Focus Mode");
-        _tagNameMap.put(TagFocusProcess, "Focus Process");
-        _tagNameMap.put(TagAfSearch, "AF Search");
-        _tagNameMap.put(TagAfAreas, "AF Areas");
-        _tagNameMap.put(TagAfPointSelected, "AF Point Selected");
-        _tagNameMap.put(TagAfFineTune, "AF Fine Tune");
-        _tagNameMap.put(TagAfFineTuneAdj, "AF Fine Tune Adj");
-
-        _tagNameMap.put(TagFlashMode, "Flash Mode");
-        _tagNameMap.put(TagFlashExposureComp, "Flash Exposure Comp");
-        _tagNameMap.put(TagFlashRemoteControl, "Flash Remote Control");
-        _tagNameMap.put(TagFlashControlMode, "Flash Control Mode");
-        _tagNameMap.put(TagFlashIntensity, "Flash Intensity");
-        _tagNameMap.put(TagManualFlashStrength, "Manual Flash Strength");
-
-        _tagNameMap.put(TagWhiteBalance2, "White Balance 2");
-        _tagNameMap.put(TagWhiteBalanceTemperature, "White Balance Temperature");
-        _tagNameMap.put(TagWhiteBalanceBracket, "White Balance Bracket");
-        _tagNameMap.put(TagCustomSaturation, "Custom Saturation");
-        _tagNameMap.put(TagModifiedSaturation, "Modified Saturation");
-        _tagNameMap.put(TagContrastSetting, "Contrast Setting");
-        _tagNameMap.put(TagSharpnessSetting, "Sharpness Setting");
-        _tagNameMap.put(TagColorSpace, "Color Space");
-        _tagNameMap.put(TagSceneMode, "Scene Mode");
-        _tagNameMap.put(TagNoiseReduction, "Noise Reduction");
-        _tagNameMap.put(TagDistortionCorrection, "Distortion Correction");
-        _tagNameMap.put(TagShadingCompensation, "Shading Compensation");
-        _tagNameMap.put(TagCompressionFactor, "Compression Factor");
-        _tagNameMap.put(TagGradation, "Gradation");
-        _tagNameMap.put(TagPictureMode, "Picture Mode");
-        _tagNameMap.put(TagPictureModeSaturation, "Picture Mode Saturation");
-        _tagNameMap.put(TagPictureModeHue, "Picture Mode Hue");
-        _tagNameMap.put(TagPictureModeContrast, "Picture Mode Contrast");
-        _tagNameMap.put(TagPictureModeSharpness, "Picture Mode Sharpness");
-        _tagNameMap.put(TagPictureModeBWFilter, "Picture Mode BW Filter");
-        _tagNameMap.put(TagPictureModeTone, "Picture Mode Tone");
-        _tagNameMap.put(TagNoiseFilter, "Noise Filter");
-        _tagNameMap.put(TagArtFilter, "Art Filter");
-        _tagNameMap.put(TagMagicFilter, "Magic Filter");
-        _tagNameMap.put(TagPictureModeEffect, "Picture Mode Effect");
-        _tagNameMap.put(TagToneLevel, "Tone Level");
-        _tagNameMap.put(TagArtFilterEffect, "Art Filter Effect");
-        _tagNameMap.put(TagColorCreatorEffect, "Color Creator Effect");
-
-        _tagNameMap.put(TagDriveMode, "Drive Mode");
-        _tagNameMap.put(TagPanoramaMode, "Panorama Mode");
-        _tagNameMap.put(TagImageQuality2, "Image Quality 2");
-        _tagNameMap.put(TagImageStabilization, "Image Stabilization");
-
-        _tagNameMap.put(TagStackedImage, "Stacked Image");
-
-        _tagNameMap.put(TagManometerPressure, "Manometer Pressure");
-        _tagNameMap.put(TagManometerReading, "Manometer Reading");
-        _tagNameMap.put(TagExtendedWBDetect, "Extended WB Detect");
-        _tagNameMap.put(TagRollAngle, "Roll Angle");
-        _tagNameMap.put(TagPitchAngle, "Pitch Angle");
-        _tagNameMap.put(TagDateTimeUtc, "Date Time UTC");
-    }
-
-    public OlympusCameraSettingsMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusCameraSettingsMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Camera Settings";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusEquipmentMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusEquipmentMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,386 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DecimalFormat;
-import java.util.HashMap;
-
-import static com.drew.metadata.exif.makernotes.OlympusEquipmentMakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusEquipmentMakernoteDirectory}.
- * <p>
- * Some Description functions and the Extender and Lens types lists converted from Exiftool version 10.10 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusEquipmentMakernoteDescriptor extends TagDescriptor<OlympusEquipmentMakernoteDirectory>
-{
-    public OlympusEquipmentMakernoteDescriptor(@NotNull OlympusEquipmentMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_EQUIPMENT_VERSION:
-                return getEquipmentVersionDescription();
-            case TAG_CAMERA_TYPE_2:
-                return getCameraType2Description();
-            case TAG_FOCAL_PLANE_DIAGONAL:
-                return getFocalPlaneDiagonalDescription();
-            case TAG_BODY_FIRMWARE_VERSION:
-                return getBodyFirmwareVersionDescription();
-            case TAG_LENS_TYPE:
-                return getLensTypeDescription();
-            case TAG_LENS_FIRMWARE_VERSION:
-                return getLensFirmwareVersionDescription();
-            case TAG_MAX_APERTURE_AT_MIN_FOCAL:
-                return getMaxApertureAtMinFocalDescription();
-            case TAG_MAX_APERTURE_AT_MAX_FOCAL:
-                return getMaxApertureAtMaxFocalDescription();
-            case TAG_MAX_APERTURE:
-                return getMaxApertureDescription();
-            case TAG_LENS_PROPERTIES:
-                return getLensPropertiesDescription();
-            case TAG_EXTENDER:
-                return getExtenderDescription();
-            case TAG_FLASH_TYPE:
-                return getFlashTypeDescription();
-            case TAG_FLASH_MODEL:
-                return getFlashModelDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getEquipmentVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_EQUIPMENT_VERSION, 4);
-    }
-
-    @Nullable
-    public String getCameraType2Description()
-    {
-        String cameratype = _directory.getString(TAG_CAMERA_TYPE_2);
-        if(cameratype == null)
-            return null;
-
-        if(OlympusMakernoteDirectory.OlympusCameraTypes.containsKey(cameratype))
-            return OlympusMakernoteDirectory.OlympusCameraTypes.get(cameratype);
-
-        return cameratype;
-    }
-
-    @Nullable
-    public String getFocalPlaneDiagonalDescription()
-    {
-        return _directory.getString(TAG_FOCAL_PLANE_DIAGONAL) + " mm";
-    }
-
-    @Nullable
-    public String getBodyFirmwareVersionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_BODY_FIRMWARE_VERSION);
-        if (value == null)
-            return null;
-
-        String hex = String.format("%04X", value);
-        return String.format("%s.%s",
-            hex.substring(0, hex.length() - 3),
-            hex.substring(hex.length() - 3));
-    }
-
-    @Nullable
-    public String getLensTypeDescription()
-    {
-        String str = _directory.getString(TAG_LENS_TYPE);
-
-        if (str == null)
-            return null;
-
-        // The String contains six numbers:
-        //
-        // - Make
-        // - Unknown
-        // - Model
-        // - Sub-model
-        // - Unknown
-        // - Unknown
-        //
-        // Only the Make, Model and Sub-model are used to identify the lens type
-        String[] values = str.split(" ");
-
-        if (values.length < 6)
-            return null;
-
-        try {
-            int num1 = Integer.parseInt(values[0]);
-            int num2 = Integer.parseInt(values[2]);
-            int num3 = Integer.parseInt(values[3]);
-            return _olympusLensTypes.get(String.format("%X %02X %02X", num1, num2, num3));
-        } catch (NumberFormatException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getLensFirmwareVersionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_LENS_FIRMWARE_VERSION);
-        if (value == null)
-            return null;
-
-        String hex = String.format("%04X", value);
-        return String.format("%s.%s",
-            hex.substring(0, hex.length() - 3),
-            hex.substring(hex.length() - 3));
-    }
-
-    @Nullable
-    public String getMaxApertureAtMinFocalDescription()
-    {
-        Integer value = _directory.getInteger(TAG_MAX_APERTURE_AT_MIN_FOCAL);
-        if (value == null)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("0.#");
-        return format.format(CalcMaxAperture(value));
-    }
-
-    @Nullable
-    public String getMaxApertureAtMaxFocalDescription()
-    {
-        Integer value = _directory.getInteger(TAG_MAX_APERTURE_AT_MAX_FOCAL);
-        if (value == null)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("0.#");
-        return format.format(CalcMaxAperture(value));
-    }
-
-    @Nullable
-    public String getMaxApertureDescription()
-    {
-        Integer value = _directory.getInteger(TAG_MAX_APERTURE);
-        if (value == null)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("0.#");
-        return format.format(CalcMaxAperture(value));
-    }
-
-    private static double CalcMaxAperture(int value)
-    {
-        return Math.pow(Math.sqrt(2.00), value / 256.0);
-    }
-
-    @Nullable
-    public String getLensPropertiesDescription()
-    {
-        Integer value = _directory.getInteger(TAG_LENS_PROPERTIES);
-        if (value == null)
-            return null;
-
-        return String.format("0x%04X", value);
-    }
-
-    @Nullable
-    public String getExtenderDescription()
-    {
-        String str = _directory.getString(TAG_EXTENDER);
-
-        if (str == null)
-            return null;
-
-        // The String contains six numbers:
-        //
-        // - Make
-        // - Unknown
-        // - Model
-        // - Sub-model
-        // - Unknown
-        // - Unknown
-        //
-        // Only the Make and Model are used to identify the extender
-        String[] values = str.split(" ");
-
-        if (values.length < 6)
-            return null;
-
-        try {
-            int num1 = Integer.parseInt(values[0]);
-            int num2 = Integer.parseInt(values[2]);
-            String extenderType = String.format("%X %02X", num1, num2);
-            return _olympusExtenderTypes.get(extenderType);
-        } catch (NumberFormatException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getFlashTypeDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_TYPE,
-            "None", null, "Simple E-System", "E-System");
-    }
-
-    @Nullable
-    public String getFlashModelDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_MODEL,
-            "None", "FL-20", "FL-50", "RF-11", "TF-22", "FL-36", "FL-50R", "FL-36R");
-    }
-
-    private static final HashMap<String, String> _olympusLensTypes = new HashMap<String, String>();
-    private static final HashMap<String, String> _olympusExtenderTypes = new HashMap<String, String>();
-
-    static {
-        _olympusLensTypes.put("0 00 00", "None");
-        // Olympus lenses (also Kenko Tokina)
-        _olympusLensTypes.put("0 01 00", "Olympus Zuiko Digital ED 50mm F2.0 Macro");
-        _olympusLensTypes.put("0 01 01", "Olympus Zuiko Digital 40-150mm F3.5-4.5"); //8
-        _olympusLensTypes.put("0 01 10", "Olympus M.Zuiko Digital ED 14-42mm F3.5-5.6"); //PH (E-P1 pre-production)
-        _olympusLensTypes.put("0 02 00", "Olympus Zuiko Digital ED 150mm F2.0");
-        _olympusLensTypes.put("0 02 10", "Olympus M.Zuiko Digital 17mm F2.8 Pancake"); //PH (E-P1 pre-production)
-        _olympusLensTypes.put("0 03 00", "Olympus Zuiko Digital ED 300mm F2.8");
-        _olympusLensTypes.put("0 03 10", "Olympus M.Zuiko Digital ED 14-150mm F4.0-5.6 [II]"); //11 (The second version of this lens seems to have the same lens ID number as the first version #20)
-        _olympusLensTypes.put("0 04 10", "Olympus M.Zuiko Digital ED 9-18mm F4.0-5.6"); //11
-        _olympusLensTypes.put("0 05 00", "Olympus Zuiko Digital 14-54mm F2.8-3.5");
-        _olympusLensTypes.put("0 05 01", "Olympus Zuiko Digital Pro ED 90-250mm F2.8"); //9
-        _olympusLensTypes.put("0 05 10", "Olympus M.Zuiko Digital ED 14-42mm F3.5-5.6 L"); //11 (E-PL1)
-        _olympusLensTypes.put("0 06 00", "Olympus Zuiko Digital ED 50-200mm F2.8-3.5");
-        _olympusLensTypes.put("0 06 01", "Olympus Zuiko Digital ED 8mm F3.5 Fisheye"); //9
-        _olympusLensTypes.put("0 06 10", "Olympus M.Zuiko Digital ED 40-150mm F4.0-5.6"); //PH
-        _olympusLensTypes.put("0 07 00", "Olympus Zuiko Digital 11-22mm F2.8-3.5");
-        _olympusLensTypes.put("0 07 01", "Olympus Zuiko Digital 18-180mm F3.5-6.3"); //6
-        _olympusLensTypes.put("0 07 10", "Olympus M.Zuiko Digital ED 12mm F2.0"); //PH
-        _olympusLensTypes.put("0 08 01", "Olympus Zuiko Digital 70-300mm F4.0-5.6"); //7 (seen as release 1 - PH)
-        _olympusLensTypes.put("0 08 10", "Olympus M.Zuiko Digital ED 75-300mm F4.8-6.7"); //PH
-        _olympusLensTypes.put("0 09 10", "Olympus M.Zuiko Digital 14-42mm F3.5-5.6 II"); //PH (E-PL2)
-        _olympusLensTypes.put("0 10 01", "Kenko Tokina Reflex 300mm F6.3 MF Macro"); //20
-        _olympusLensTypes.put("0 10 10", "Olympus M.Zuiko Digital ED 12-50mm F3.5-6.3 EZ"); //PH
-        _olympusLensTypes.put("0 11 10", "Olympus M.Zuiko Digital 45mm F1.8"); //17
-        _olympusLensTypes.put("0 12 10", "Olympus M.Zuiko Digital ED 60mm F2.8 Macro"); //20
-        _olympusLensTypes.put("0 13 10", "Olympus M.Zuiko Digital 14-42mm F3.5-5.6 II R"); //PH/20
-        _olympusLensTypes.put("0 14 10", "Olympus M.Zuiko Digital ED 40-150mm F4.0-5.6 R"); //19
-        // '0 14 10.1", "Olympus M.Zuiko Digital ED 14-150mm F4.0-5.6 II"); //11 (questionable & unconfirmed -- all samples I can find are '0 3 10' - PH)
-        _olympusLensTypes.put("0 15 00", "Olympus Zuiko Digital ED 7-14mm F4.0");
-        _olympusLensTypes.put("0 15 10", "Olympus M.Zuiko Digital ED 75mm F1.8"); //PH
-        _olympusLensTypes.put("0 16 10", "Olympus M.Zuiko Digital 17mm F1.8"); //20
-        _olympusLensTypes.put("0 17 00", "Olympus Zuiko Digital Pro ED 35-100mm F2.0"); //7
-        _olympusLensTypes.put("0 18 00", "Olympus Zuiko Digital 14-45mm F3.5-5.6");
-        _olympusLensTypes.put("0 18 10", "Olympus M.Zuiko Digital ED 75-300mm F4.8-6.7 II"); //20
-        _olympusLensTypes.put("0 19 10", "Olympus M.Zuiko Digital ED 12-40mm F2.8 Pro"); //PH
-        _olympusLensTypes.put("0 20 00", "Olympus Zuiko Digital 35mm F3.5 Macro"); //9
-        _olympusLensTypes.put("0 20 10", "Olympus M.Zuiko Digital ED 40-150mm F2.8 Pro"); //20
-        _olympusLensTypes.put("0 21 10", "Olympus M.Zuiko Digital ED 14-42mm F3.5-5.6 EZ"); //20
-        _olympusLensTypes.put("0 22 00", "Olympus Zuiko Digital 17.5-45mm F3.5-5.6"); //9
-        _olympusLensTypes.put("0 22 10", "Olympus M.Zuiko Digital 25mm F1.8"); //20
-        _olympusLensTypes.put("0 23 00", "Olympus Zuiko Digital ED 14-42mm F3.5-5.6"); //PH
-        _olympusLensTypes.put("0 23 10", "Olympus M.Zuiko Digital ED 7-14mm F2.8 Pro"); //20
-        _olympusLensTypes.put("0 24 00", "Olympus Zuiko Digital ED 40-150mm F4.0-5.6"); //PH
-        _olympusLensTypes.put("0 24 10", "Olympus M.Zuiko Digital ED 300mm F4.0 IS Pro"); //20
-        _olympusLensTypes.put("0 25 10", "Olympus M.Zuiko Digital ED 8mm F1.8 Fisheye Pro"); //20
-        _olympusLensTypes.put("0 30 00", "Olympus Zuiko Digital ED 50-200mm F2.8-3.5 SWD"); //7
-        _olympusLensTypes.put("0 31 00", "Olympus Zuiko Digital ED 12-60mm F2.8-4.0 SWD"); //7
-        _olympusLensTypes.put("0 32 00", "Olympus Zuiko Digital ED 14-35mm F2.0 SWD"); //PH
-        _olympusLensTypes.put("0 33 00", "Olympus Zuiko Digital 25mm F2.8"); //PH
-        _olympusLensTypes.put("0 34 00", "Olympus Zuiko Digital ED 9-18mm F4.0-5.6"); //7
-        _olympusLensTypes.put("0 35 00", "Olympus Zuiko Digital 14-54mm F2.8-3.5 II"); //PH
-        // Sigma lenses
-        _olympusLensTypes.put("1 01 00", "Sigma 18-50mm F3.5-5.6 DC"); //8
-        _olympusLensTypes.put("1 01 10", "Sigma 30mm F2.8 EX DN"); //20
-        _olympusLensTypes.put("1 02 00", "Sigma 55-200mm F4.0-5.6 DC");
-        _olympusLensTypes.put("1 02 10", "Sigma 19mm F2.8 EX DN"); //20
-        _olympusLensTypes.put("1 03 00", "Sigma 18-125mm F3.5-5.6 DC");
-        _olympusLensTypes.put("1 03 10", "Sigma 30mm F2.8 DN | A"); //20
-        _olympusLensTypes.put("1 04 00", "Sigma 18-125mm F3.5-5.6 DC"); //7
-        _olympusLensTypes.put("1 04 10", "Sigma 19mm F2.8 DN | A"); //20
-        _olympusLensTypes.put("1 05 00", "Sigma 30mm F1.4 EX DC HSM"); //10
-        _olympusLensTypes.put("1 05 10", "Sigma 60mm F2.8 DN | A"); //20
-        _olympusLensTypes.put("1 06 00", "Sigma APO 50-500mm F4.0-6.3 EX DG HSM"); //6
-        _olympusLensTypes.put("1 07 00", "Sigma Macro 105mm F2.8 EX DG"); //PH
-        _olympusLensTypes.put("1 08 00", "Sigma APO Macro 150mm F2.8 EX DG HSM"); //PH
-        _olympusLensTypes.put("1 09 00", "Sigma 18-50mm F2.8 EX DC Macro"); //20
-        _olympusLensTypes.put("1 10 00", "Sigma 24mm F1.8 EX DG Aspherical Macro"); //PH
-        _olympusLensTypes.put("1 11 00", "Sigma APO 135-400mm F4.5-5.6 DG"); //11
-        _olympusLensTypes.put("1 12 00", "Sigma APO 300-800mm F5.6 EX DG HSM"); //11
-        _olympusLensTypes.put("1 13 00", "Sigma 30mm F1.4 EX DC HSM"); //11
-        _olympusLensTypes.put("1 14 00", "Sigma APO 50-500mm F4.0-6.3 EX DG HSM"); //11
-        _olympusLensTypes.put("1 15 00", "Sigma 10-20mm F4.0-5.6 EX DC HSM"); //11
-        _olympusLensTypes.put("1 16 00", "Sigma APO 70-200mm F2.8 II EX DG Macro HSM"); //11
-        _olympusLensTypes.put("1 17 00", "Sigma 50mm F1.4 EX DG HSM"); //11
-        // Panasonic/Leica lenses
-        _olympusLensTypes.put("2 01 00", "Leica D Vario Elmarit 14-50mm F2.8-3.5 Asph."); //11
-        _olympusLensTypes.put("2 01 10", "Lumix G Vario 14-45mm F3.5-5.6 Asph. Mega OIS"); //16
-        _olympusLensTypes.put("2 02 00", "Leica D Summilux 25mm F1.4 Asph."); //11
-        _olympusLensTypes.put("2 02 10", "Lumix G Vario 45-200mm F4.0-5.6 Mega OIS"); //16
-        _olympusLensTypes.put("2 03 00", "Leica D Vario Elmar 14-50mm F3.8-5.6 Asph. Mega OIS"); //11
-        _olympusLensTypes.put("2 03 01", "Leica D Vario Elmar 14-50mm F3.8-5.6 Asph."); //14 (L10 kit)
-        _olympusLensTypes.put("2 03 10", "Lumix G Vario HD 14-140mm F4.0-5.8 Asph. Mega OIS"); //16
-        _olympusLensTypes.put("2 04 00", "Leica D Vario Elmar 14-150mm F3.5-5.6"); //13
-        _olympusLensTypes.put("2 04 10", "Lumix G Vario 7-14mm F4.0 Asph."); //PH (E-P1 pre-production)
-        _olympusLensTypes.put("2 05 10", "Lumix G 20mm F1.7 Asph."); //16
-        _olympusLensTypes.put("2 06 10", "Leica DG Macro-Elmarit 45mm F2.8 Asph. Mega OIS"); //PH
-        _olympusLensTypes.put("2 07 10", "Lumix G Vario 14-42mm F3.5-5.6 Asph. Mega OIS"); //20
-        _olympusLensTypes.put("2 08 10", "Lumix G Fisheye 8mm F3.5"); //PH
-        _olympusLensTypes.put("2 09 10", "Lumix G Vario 100-300mm F4.0-5.6 Mega OIS"); //11
-        _olympusLensTypes.put("2 10 10", "Lumix G 14mm F2.5 Asph."); //17
-        _olympusLensTypes.put("2 11 10", "Lumix G 12.5mm F12 3D"); //20 (H-FT012)
-        _olympusLensTypes.put("2 12 10", "Leica DG Summilux 25mm F1.4 Asph."); //20
-        _olympusLensTypes.put("2 13 10", "Lumix G X Vario PZ 45-175mm F4.0-5.6 Asph. Power OIS"); //20
-        _olympusLensTypes.put("2 14 10", "Lumix G X Vario PZ 14-42mm F3.5-5.6 Asph. Power OIS"); //20
-        _olympusLensTypes.put("2 15 10", "Lumix G X Vario 12-35mm F2.8 Asph. Power OIS"); //PH
-        _olympusLensTypes.put("2 16 10", "Lumix G Vario 45-150mm F4.0-5.6 Asph. Mega OIS"); //20
-        _olympusLensTypes.put("2 17 10", "Lumix G X Vario 35-100mm F2.8 Power OIS"); //PH
-        _olympusLensTypes.put("2 18 10", "Lumix G Vario 14-42mm F3.5-5.6 II Asph. Mega OIS"); //20
-        _olympusLensTypes.put("2 19 10", "Lumix G Vario 14-140mm F3.5-5.6 Asph. Power OIS"); //20
-        _olympusLensTypes.put("2 20 10", "Lumix G Vario 12-32mm F3.5-5.6 Asph. Mega OIS"); //20
-        _olympusLensTypes.put("2 21 10", "Leica DG Nocticron 42.5mm F1.2 Asph. Power OIS"); //20
-        _olympusLensTypes.put("2 22 10", "Leica DG Summilux 15mm F1.7 Asph."); //20
-        // '2 23 10", "Lumix G Vario 35-100mm F4.0-5.6 Asph. Mega OIS"); //20 (guess)
-        _olympusLensTypes.put("2 24 10", "Lumix G Macro 30mm F2.8 Asph. Mega OIS"); //20
-        _olympusLensTypes.put("2 25 10", "Lumix G 42.5mm F1.7 Asph. Power OIS"); //20
-        _olympusLensTypes.put("3 01 00", "Leica D Vario Elmarit 14-50mm F2.8-3.5 Asph."); //11
-        _olympusLensTypes.put("3 02 00", "Leica D Summilux 25mm F1.4 Asph."); //11
-        // Tamron lenses
-        _olympusLensTypes.put("5 01 10", "Tamron 14-150mm F3.5-5.8 Di III"); //20 (model C001)
-
-
-        _olympusExtenderTypes.put("0 00", "None");
-        _olympusExtenderTypes.put("0 04", "Olympus Zuiko Digital EC-14 1.4x Teleconverter");
-        _olympusExtenderTypes.put("0 08", "Olympus EX-25 Extension Tube");
-        _olympusExtenderTypes.put("0 10", "Olympus Zuiko Digital EC-20 2.0x Teleconverter");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusEquipmentMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusEquipmentMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,118 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * The Olympus equipment makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusEquipmentMakernoteDirectory extends Directory
-{
-    public static final int TAG_EQUIPMENT_VERSION = 0x0000;
-    public static final int TAG_CAMERA_TYPE_2 = 0x0100;
-    public static final int TAG_SERIAL_NUMBER = 0x0101;
-
-    public static final int TAG_INTERNAL_SERIAL_NUMBER = 0x0102;
-    public static final int TAG_FOCAL_PLANE_DIAGONAL = 0x0103;
-    public static final int TAG_BODY_FIRMWARE_VERSION = 0x0104;
-
-    public static final int TAG_LENS_TYPE = 0x0201;
-    public static final int TAG_LENS_SERIAL_NUMBER = 0x0202;
-    public static final int TAG_LENS_MODEL = 0x0203;
-    public static final int TAG_LENS_FIRMWARE_VERSION = 0x0204;
-    public static final int TAG_MAX_APERTURE_AT_MIN_FOCAL = 0x0205;
-    public static final int TAG_MAX_APERTURE_AT_MAX_FOCAL = 0x0206;
-    public static final int TAG_MIN_FOCAL_LENGTH = 0x0207;
-    public static final int TAG_MAX_FOCAL_LENGTH = 0x0208;
-    public static final int TAG_MAX_APERTURE = 0x020A;
-    public static final int TAG_LENS_PROPERTIES = 0x020B;
-
-    public static final int TAG_EXTENDER = 0x0301;
-    public static final int TAG_EXTENDER_SERIAL_NUMBER = 0x0302;
-    public static final int TAG_EXTENDER_MODEL = 0x0303;
-    public static final int TAG_EXTENDER_FIRMWARE_VERSION = 0x0304;
-
-    public static final int TAG_CONVERSION_LENS = 0x0403;
-
-    public static final int TAG_FLASH_TYPE = 0x1000;
-    public static final int TAG_FLASH_MODEL = 0x1001;
-    public static final int TAG_FLASH_FIRMWARE_VERSION = 0x1002;
-    public static final int TAG_FLASH_SERIAL_NUMBER = 0x1003;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_EQUIPMENT_VERSION, "Equipment Version");
-        _tagNameMap.put(TAG_CAMERA_TYPE_2, "Camera Type 2");
-        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
-        _tagNameMap.put(TAG_INTERNAL_SERIAL_NUMBER, "Internal Serial Number");
-        _tagNameMap.put(TAG_FOCAL_PLANE_DIAGONAL, "Focal Plane Diagonal");
-        _tagNameMap.put(TAG_BODY_FIRMWARE_VERSION, "Body Firmware Version");
-        _tagNameMap.put(TAG_LENS_TYPE, "Lens Type");
-        _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
-        _tagNameMap.put(TAG_LENS_MODEL, "Lens Model");
-        _tagNameMap.put(TAG_LENS_FIRMWARE_VERSION, "Lens Firmware Version");
-        _tagNameMap.put(TAG_MAX_APERTURE_AT_MIN_FOCAL, "Max Aperture At Min Focal");
-        _tagNameMap.put(TAG_MAX_APERTURE_AT_MAX_FOCAL, "Max Aperture At Max Focal");
-        _tagNameMap.put(TAG_MIN_FOCAL_LENGTH, "Min Focal Length");
-        _tagNameMap.put(TAG_MAX_FOCAL_LENGTH, "Max Focal Length");
-        _tagNameMap.put(TAG_MAX_APERTURE, "Max Aperture");
-        _tagNameMap.put(TAG_LENS_PROPERTIES, "Lens Properties");
-        _tagNameMap.put(TAG_EXTENDER, "Extender");
-        _tagNameMap.put(TAG_EXTENDER_SERIAL_NUMBER, "Extender Serial Number");
-        _tagNameMap.put(TAG_EXTENDER_MODEL, "Extender Model");
-        _tagNameMap.put(TAG_EXTENDER_FIRMWARE_VERSION, "Extender Firmware Version");
-        _tagNameMap.put(TAG_CONVERSION_LENS, "Conversion Lens");
-        _tagNameMap.put(TAG_FLASH_TYPE, "Flash Type");
-        _tagNameMap.put(TAG_FLASH_MODEL, "Flash Model");
-        _tagNameMap.put(TAG_FLASH_FIRMWARE_VERSION, "Flash Firmware Version");
-        _tagNameMap.put(TAG_FLASH_SERIAL_NUMBER, "Flash Serial Number");
-    }
-
-    public OlympusEquipmentMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusEquipmentMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Equipment";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusFocusInfoMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusFocusInfoMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,217 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.OlympusFocusInfoMakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusFocusInfoMakernoteDirectory}.
- * <p>
- * Some Description functions converted from Exiftool version 10.10 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusFocusInfoMakernoteDescriptor extends TagDescriptor<OlympusFocusInfoMakernoteDirectory>
-{
-    public OlympusFocusInfoMakernoteDescriptor(@NotNull OlympusFocusInfoMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagFocusInfoVersion:
-                return getFocusInfoVersionDescription();
-            case TagAutoFocus:
-                return getAutoFocusDescription();
-            case TagFocusDistance:
-                return getFocusDistanceDescription();
-            case TagAfPoint:
-                return getAfPointDescription();
-            case TagExternalFlash:
-                return getExternalFlashDescription();
-            case TagExternalFlashBounce:
-                return getExternalFlashBounceDescription();
-            case TagExternalFlashZoom:
-                return getExternalFlashZoomDescription();
-            case TagManualFlash:
-                return getManualFlashDescription();
-            case TagMacroLed:
-                return getMacroLedDescription();
-            case TagSensorTemperature:
-                return getSensorTemperatureDescription();
-            case TagImageStabilization:
-                return getImageStabilizationDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getFocusInfoVersionDescription()
-    {
-        return getVersionBytesDescription(TagFocusInfoVersion, 4);
-    }
-
-    @Nullable
-    public String getAutoFocusDescription()
-    {
-        return getIndexedDescription(TagAutoFocus,
-            "Off", "On");
-    }
-
-    @Nullable
-    public String getFocusDistanceDescription()
-    {
-        Rational value = _directory.getRational(TagFocusDistance);
-        if (value == null)
-            return "inf";
-        if (value.getNumerator() == 0xFFFFFFFFL || value.getNumerator() == 0x00000000L)
-            return "inf";
-
-        return value.getNumerator() / 1000.0 + " m";
-    }
-
-    @Nullable
-    public String getAfPointDescription()
-    {
-        Integer value = _directory.getInteger(TagAfPoint);
-        if (value == null)
-            return null;
-
-        return value.toString();
-    }
-
-    @Nullable
-    public String getExternalFlashDescription()
-    {
-        int[] values = _directory.getIntArray(TagExternalFlash);
-        if (values == null || values.length < 2)
-            return null;
-
-        String join = String.format("%d %d", (short)values[0], (short)values[1]);
-
-        if(join.equals("0 0"))
-            return "Off";
-        else if(join.equals("1 0"))
-            return "On";
-        else
-            return "Unknown (" + join + ")";
-    }
-
-    @Nullable
-    public String getExternalFlashBounceDescription()
-    {
-        return getIndexedDescription(TagExternalFlashBounce,
-                "Bounce or Off", "Direct");
-    }
-
-    @Nullable
-    public String getExternalFlashZoomDescription()
-    {
-        int[] values = _directory.getIntArray(TagExternalFlashZoom);
-        if (values == null)
-        {
-            // check if it's only one value long also
-            Integer value = _directory.getInteger(TagExternalFlashZoom);
-            if(value == null)
-                return null;
-
-            values = new int[1];
-            values[0] = value;
-        }
-
-        if (values.length == 0)
-            return null;
-
-        String join = String.format("%d", (short)values[0]);
-        if(values.length > 1)
-            join += " " + String.format("%d", (short)values[1]);
-
-        if(join.equals("0"))
-            return "Off";
-        else if(join.equals("1"))
-            return "On";
-        else if(join.equals("0 0"))
-            return "Off";
-        else if(join.equals("1 0"))
-            return "On";
-        else
-            return "Unknown (" + join + ")";
-
-    }
-
-    @Nullable
-    public String getManualFlashDescription()
-    {
-        int[] values = _directory.getIntArray(TagManualFlash);
-        if (values == null)
-            return null;
-
-        if ((short)values[0] == 0)
-            return "Off";
-
-        if ((short)values[1] == 1)
-            return "Full";
-        return "On (1/" + (short)values[1] + " strength)";
-    }
-
-    @Nullable
-    public String getMacroLedDescription()
-    {
-        return getIndexedDescription(TagMacroLed,
-                "Off", "On");
-    }
-
-    /// <remarks>
-    /// <para>TODO: Complete when Camera Model is available.</para>
-    /// <para>There are differences in how to interpret this tag that can only be reconciled by knowing the model.</para>
-    /// </remarks>
-    @Nullable
-    public String getSensorTemperatureDescription()
-    {
-        return _directory.getString(TagSensorTemperature);
-    }
-
-    @Nullable
-    public String getImageStabilizationDescription()
-    {
-        byte[] values = _directory.getByteArray(TagImageStabilization);
-        if (values == null)
-            return null;
-
-        if((values[0] | values[1] | values[2] | values[3]) == 0x0)
-            return "Off";
-        return "On, " + ((values[43] & 1) > 0 ? "Mode 1" : "Mode 2");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusFocusInfoMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusFocusInfoMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,110 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * The Olympus focus info makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusFocusInfoMakernoteDirectory extends Directory
-{
-    public static final int TagFocusInfoVersion = 0x0000;
-    public static final int TagAutoFocus = 0x0209;
-    public static final int TagSceneDetect = 0x0210;
-    public static final int TagSceneArea = 0x0211;
-    public static final int TagSceneDetectData = 0x0212;
-
-    public static final int TagZoomStepCount = 0x0300;
-    public static final int TagFocusStepCount = 0x0301;
-    public static final int TagFocusStepInfinity = 0x0303;
-    public static final int TagFocusStepNear = 0x0304;
-    public static final int TagFocusDistance = 0x0305;
-    public static final int TagAfPoint = 0x0308;
-    // 0x031a Continuous AF parameters?
-    public static final int TagAfInfo = 0x0328;    // ifd
-
-    public static final int TagExternalFlash = 0x1201;
-    public static final int TagExternalFlashGuideNumber = 0x1203;
-    public static final int TagExternalFlashBounce = 0x1204;
-    public static final int TagExternalFlashZoom = 0x1205;
-    public static final int TagInternalFlash = 0x1208;
-    public static final int TagManualFlash = 0x1209;
-    public static final int TagMacroLed = 0x120A;
-
-    public static final int TagSensorTemperature = 0x1500;
-
-    public static final int TagImageStabilization = 0x1600;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TagFocusInfoVersion, "Focus Info Version");
-        _tagNameMap.put(TagAutoFocus, "Auto Focus");
-        _tagNameMap.put(TagSceneDetect, "Scene Detect");
-        _tagNameMap.put(TagSceneArea, "Scene Area");
-        _tagNameMap.put(TagSceneDetectData, "Scene Detect Data");
-        _tagNameMap.put(TagZoomStepCount, "Zoom Step Count");
-        _tagNameMap.put(TagFocusStepCount, "Focus Step Count");
-        _tagNameMap.put(TagFocusStepInfinity, "Focus Step Infinity");
-        _tagNameMap.put(TagFocusStepNear, "Focus Step Near");
-        _tagNameMap.put(TagFocusDistance, "Focus Distance");
-        _tagNameMap.put(TagAfPoint, "AF Point");
-        _tagNameMap.put(TagAfInfo, "AF Info");
-        _tagNameMap.put(TagExternalFlash, "External Flash");
-        _tagNameMap.put(TagExternalFlashGuideNumber, "External Flash Guide Number");
-        _tagNameMap.put(TagExternalFlashBounce, "External Flash Bounce");
-        _tagNameMap.put(TagExternalFlashZoom, "External Flash Zoom");
-        _tagNameMap.put(TagInternalFlash, "Internal Flash");
-        _tagNameMap.put(TagManualFlash, "Manual Flash");
-        _tagNameMap.put(TagMacroLed, "Macro LED");
-        _tagNameMap.put(TagSensorTemperature, "Sensor Temperature");
-        _tagNameMap.put(TagImageStabilization, "Image Stabilization");
-    }
-
-    public OlympusFocusInfoMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusFocusInfoMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Focus Info";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusImageProcessingMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusImageProcessingMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,240 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusImageProcessingMakernoteDirectory}.
- * <p>
- * Some Description functions converted from Exiftool version 10.33 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusImageProcessingMakernoteDescriptor extends TagDescriptor<OlympusImageProcessingMakernoteDirectory>
-{
-    public OlympusImageProcessingMakernoteDescriptor(@NotNull OlympusImageProcessingMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagImageProcessingVersion:
-                return getImageProcessingVersionDescription();
-            case TagColorMatrix:
-                return getColorMatrixDescription();
-            case TagNoiseReduction2:
-                return getNoiseReduction2Description();
-            case TagDistortionCorrection2:
-                return getDistortionCorrection2Description();
-            case TagShadingCompensation2:
-                return getShadingCompensation2Description();
-            case TagMultipleExposureMode:
-                return getMultipleExposureModeDescription();
-            case TagAspectRatio:
-                return getAspectRatioDescription();
-            case TagKeystoneCompensation:
-                return getKeystoneCompensationDescription();
-            case TagKeystoneDirection:
-                return getKeystoneDirectionDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getImageProcessingVersionDescription()
-    {
-        return getVersionBytesDescription(TagImageProcessingVersion, 4);
-    }
-
-    @Nullable
-    public String getColorMatrixDescription()
-    {
-        int[] obj = _directory.getIntArray(TagColorMatrix);
-        if (obj == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < obj.length; i++) {
-            if (i != 0)
-                sb.append(" ");
-            sb.append((short)obj[i]);
-        }
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getNoiseReduction2Description()
-    {
-        Integer value = _directory.getInteger(TagNoiseReduction2);
-        if (value == null)
-            return null;
-
-        if (value == 0)
-            return "(none)";
-
-        StringBuilder sb = new StringBuilder();
-        short v = value.shortValue();
-
-        if (( v       & 1) != 0) sb.append("Noise Reduction, ");
-        if (((v >> 1) & 1) != 0) sb.append("Noise Filter, ");
-        if (((v >> 2) & 1) != 0) sb.append("Noise Filter (ISO Boost), ");
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getDistortionCorrection2Description()
-    {
-        return getIndexedDescription(TagDistortionCorrection2, "Off", "On");
-    }
-
-    @Nullable
-    public String getShadingCompensation2Description()
-    {
-        return getIndexedDescription(TagShadingCompensation2, "Off", "On");
-    }
-
-    @Nullable
-    public String getMultipleExposureModeDescription()
-    {
-        int[] values = _directory.getIntArray(TagMultipleExposureMode);
-        if (values == null)
-        {
-            // check if it's only one value long also
-            Integer value = _directory.getInteger(TagMultipleExposureMode);
-            if(value == null)
-                return null;
-
-            values = new int[1];
-            values[0] = value;
-        }
-
-        if (values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-
-        switch ((short)values[0])
-        {
-            case 0:
-                sb.append("Off");
-                break;
-            case 2:
-                sb.append("On (2 frames)");
-                break;
-            case 3:
-                sb.append("On (3 frames)");
-                break;
-            default:
-                sb.append("Unknown (").append((short)values[0]).append(")");
-                break;
-        }
-
-        if (values.length > 1)
-            sb.append("; ").append((short)values[1]);
-
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getAspectRatioDescription()
-    {
-        byte[] values = _directory.getByteArray(TagAspectRatio);
-        if (values == null || values.length < 2)
-            return null;
-
-        String join = String.format("%d %d", values[0], values[1]);
-
-        String ret;
-        if(join.equals("1 1"))
-            ret = "4:3";
-        else if(join.equals("1 4"))
-            ret = "1:1";
-        else if(join.equals("2 1"))
-            ret = "3:2 (RAW)";
-        else if(join.equals("2 2"))
-            ret = "3:2";
-        else if(join.equals("3 1"))
-            ret = "16:9 (RAW)";
-        else if(join.equals("3 3"))
-            ret = "16:9";
-        else if(join.equals("4 1"))
-            ret = "1:1 (RAW)";
-        else if(join.equals("4 4"))
-            ret = "6:6";
-        else if(join.equals("5 5"))
-            ret = "5:4";
-        else if(join.equals("6 6"))
-            ret = "7:6";
-        else if(join.equals("7 7"))
-            ret = "6:5";
-        else if(join.equals("8 8"))
-            ret = "7:5";
-        else if(join.equals("9 1"))
-            ret = "3:4 (RAW)";
-        else if(join.equals("9 9"))
-            ret = "3:4";
-        else
-            ret = "Unknown (" + join + ")";
-
-        return ret;
-    }
-
-    @Nullable
-    public String getKeystoneCompensationDescription()
-    {
-        byte[] values = _directory.getByteArray(TagKeystoneCompensation);
-        if (values == null || values.length < 2)
-            return null;
-
-        String join = String.format("%d %d", values[0], values[1]);
-
-        String ret;
-        if(join.equals("0 0"))
-            ret = "Off";
-        else if(join.equals("0 1"))
-            ret = "On";
-        else
-            ret = "Unknown (" + join + ")";
-
-        return ret;
-    }
-
-    @Nullable
-    public String getKeystoneDirectionDescription()
-    {
-        return getIndexedDescription(TagKeystoneDirection, "Vertical", "Horizontal");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusImageProcessingMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusImageProcessingMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,212 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * The Olympus image processing makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusImageProcessingMakernoteDirectory extends Directory
-{
-    public static final int TagImageProcessingVersion = 0x0000;
-    public static final int TagWbRbLevels = 0x0100;
-    // 0x0101 - in-camera AutoWB unless it is all 0's or all 256's (ref IB)
-    public static final int TagWbRbLevels3000K = 0x0102;
-    public static final int TagWbRbLevels3300K = 0x0103;
-    public static final int TagWbRbLevels3600K = 0x0104;
-    public static final int TagWbRbLevels3900K = 0x0105;
-    public static final int TagWbRbLevels4000K = 0x0106;
-    public static final int TagWbRbLevels4300K = 0x0107;
-    public static final int TagWbRbLevels4500K = 0x0108;
-    public static final int TagWbRbLevels4800K = 0x0109;
-    public static final int TagWbRbLevels5300K = 0x010a;
-    public static final int TagWbRbLevels6000K = 0x010b;
-    public static final int TagWbRbLevels6600K = 0x010c;
-    public static final int TagWbRbLevels7500K = 0x010d;
-    public static final int TagWbRbLevelsCwB1 = 0x010e;
-    public static final int TagWbRbLevelsCwB2 = 0x010f;
-    public static final int TagWbRbLevelsCwB3 = 0x0110;
-    public static final int TagWbRbLevelsCwB4 = 0x0111;
-    public static final int TagWbGLevel3000K = 0x0113;
-    public static final int TagWbGLevel3300K = 0x0114;
-    public static final int TagWbGLevel3600K = 0x0115;
-    public static final int TagWbGLevel3900K = 0x0116;
-    public static final int TagWbGLevel4000K = 0x0117;
-    public static final int TagWbGLevel4300K = 0x0118;
-    public static final int TagWbGLevel4500K = 0x0119;
-    public static final int TagWbGLevel4800K = 0x011a;
-    public static final int TagWbGLevel5300K = 0x011b;
-    public static final int TagWbGLevel6000K = 0x011c;
-    public static final int TagWbGLevel6600K = 0x011d;
-    public static final int TagWbGLevel7500K = 0x011e;
-    public static final int TagWbGLevel = 0x011f;
-    // 0x0121 = WB preset for flash (about 6000K) (ref IB)
-    // 0x0125 = WB preset for underwater (ref IB)
-
-    public static final int TagColorMatrix = 0x0200;
-    // color matrices (ref 11):
-    // 0x0201-0x020d are sRGB color matrices
-    // 0x020e-0x021a are Adobe RGB color matrices
-    // 0x021b-0x0227 are ProPhoto RGB color matrices
-    // 0x0228 and 0x0229 are ColorMatrix for E-330
-    // 0x0250-0x0252 are sRGB color matrices
-    // 0x0253-0x0255 are Adobe RGB color matrices
-    // 0x0256-0x0258 are ProPhoto RGB color matrices
-
-    public static final int TagEnhancer = 0x0300;
-    public static final int TagEnhancerValues = 0x0301;
-    public static final int TagCoringFilter = 0x0310;
-    public static final int TagCoringValues = 0x0311;
-    public static final int TagBlackLevel2 = 0x0600;
-    public static final int TagGainBase = 0x0610;
-    public static final int TagValidBits = 0x0611;
-    public static final int TagCropLeft = 0x0612;
-    public static final int TagCropTop = 0x0613;
-    public static final int TagCropWidth = 0x0614;
-    public static final int TagCropHeight = 0x0615;
-    public static final int TagUnknownBlock1 = 0x0635;
-    public static final int TagUnknownBlock2 = 0x0636;
-
-    // 0x0800 LensDistortionParams, float[9] (ref 11)
-    // 0x0801 LensShadingParams, int16u[16] (ref 11)
-    public static final int TagSensorCalibration = 0x0805;
-
-    public static final int TagNoiseReduction2 = 0x1010;
-    public static final int TagDistortionCorrection2 = 0x1011;
-    public static final int TagShadingCompensation2 = 0x1012;
-    public static final int TagMultipleExposureMode = 0x101c;
-    public static final int TagUnknownBlock3 = 0x1103;
-    public static final int TagUnknownBlock4 = 0x1104;
-    public static final int TagAspectRatio = 0x1112;
-    public static final int TagAspectFrame = 0x1113;
-    public static final int TagFacesDetected = 0x1200;
-    public static final int TagFaceDetectArea = 0x1201;
-    public static final int TagMaxFaces = 0x1202;
-    public static final int TagFaceDetectFrameSize = 0x1203;
-    public static final int TagFaceDetectFrameCrop = 0x1207;
-    public static final int TagCameraTemperature = 0x1306;
-
-    public static final int TagKeystoneCompensation = 0x1900;
-    public static final int TagKeystoneDirection = 0x1901;
-    // 0x1905 - focal length (PH, E-M1)
-    public static final int TagKeystoneValue = 0x1906;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TagImageProcessingVersion, "Image Processing Version");
-        _tagNameMap.put(TagWbRbLevels, "WB RB Levels");
-        _tagNameMap.put(TagWbRbLevels3000K, "WB RB Levels 3000K");
-        _tagNameMap.put(TagWbRbLevels3300K, "WB RB Levels 3300K");
-        _tagNameMap.put(TagWbRbLevels3600K, "WB RB Levels 3600K");
-        _tagNameMap.put(TagWbRbLevels3900K, "WB RB Levels 3900K");
-        _tagNameMap.put(TagWbRbLevels4000K, "WB RB Levels 4000K");
-        _tagNameMap.put(TagWbRbLevels4300K, "WB RB Levels 4300K");
-        _tagNameMap.put(TagWbRbLevels4500K, "WB RB Levels 4500K");
-        _tagNameMap.put(TagWbRbLevels4800K, "WB RB Levels 4800K");
-        _tagNameMap.put(TagWbRbLevels5300K, "WB RB Levels 5300K");
-        _tagNameMap.put(TagWbRbLevels6000K, "WB RB Levels 6000K");
-        _tagNameMap.put(TagWbRbLevels6600K, "WB RB Levels 6600K");
-        _tagNameMap.put(TagWbRbLevels7500K, "WB RB Levels 7500K");
-        _tagNameMap.put(TagWbRbLevelsCwB1, "WB RB Levels CWB1");
-        _tagNameMap.put(TagWbRbLevelsCwB2, "WB RB Levels CWB2");
-        _tagNameMap.put(TagWbRbLevelsCwB3, "WB RB Levels CWB3");
-        _tagNameMap.put(TagWbRbLevelsCwB4, "WB RB Levels CWB4");
-        _tagNameMap.put(TagWbGLevel3000K, "WB G Level 3000K");
-        _tagNameMap.put(TagWbGLevel3300K, "WB G Level 3300K");
-        _tagNameMap.put(TagWbGLevel3600K, "WB G Level 3600K");
-        _tagNameMap.put(TagWbGLevel3900K, "WB G Level 3900K");
-        _tagNameMap.put(TagWbGLevel4000K, "WB G Level 4000K");
-        _tagNameMap.put(TagWbGLevel4300K, "WB G Level 4300K");
-        _tagNameMap.put(TagWbGLevel4500K, "WB G Level 4500K");
-        _tagNameMap.put(TagWbGLevel4800K, "WB G Level 4800K");
-        _tagNameMap.put(TagWbGLevel5300K, "WB G Level 5300K");
-        _tagNameMap.put(TagWbGLevel6000K, "WB G Level 6000K");
-        _tagNameMap.put(TagWbGLevel6600K, "WB G Level 6600K");
-        _tagNameMap.put(TagWbGLevel7500K, "WB G Level 7500K");
-        _tagNameMap.put(TagWbGLevel, "WB G Level");
-
-        _tagNameMap.put(TagColorMatrix, "Color Matrix");
-
-        _tagNameMap.put(TagEnhancer, "Enhancer");
-        _tagNameMap.put(TagEnhancerValues, "Enhancer Values");
-        _tagNameMap.put(TagCoringFilter, "Coring Filter");
-        _tagNameMap.put(TagCoringValues, "Coring Values");
-        _tagNameMap.put(TagBlackLevel2, "Black Level 2");
-        _tagNameMap.put(TagGainBase, "Gain Base");
-        _tagNameMap.put(TagValidBits, "Valid Bits");
-        _tagNameMap.put(TagCropLeft, "Crop Left");
-        _tagNameMap.put(TagCropTop, "Crop Top");
-        _tagNameMap.put(TagCropWidth, "Crop Width");
-        _tagNameMap.put(TagCropHeight, "Crop Height");
-        _tagNameMap.put(TagUnknownBlock1, "Unknown Block 1");
-        _tagNameMap.put(TagUnknownBlock2, "Unknown Block 2");
-
-        _tagNameMap.put(TagSensorCalibration, "Sensor Calibration");
-
-        _tagNameMap.put(TagNoiseReduction2, "Noise Reduction 2");
-        _tagNameMap.put(TagDistortionCorrection2, "Distortion Correction 2");
-        _tagNameMap.put(TagShadingCompensation2, "Shading Compensation 2");
-        _tagNameMap.put(TagMultipleExposureMode, "Multiple Exposure Mode");
-        _tagNameMap.put(TagUnknownBlock3, "Unknown Block 3");
-        _tagNameMap.put(TagUnknownBlock4, "Unknown Block 4");
-        _tagNameMap.put(TagAspectRatio, "Aspect Ratio");
-        _tagNameMap.put(TagAspectFrame, "Aspect Frame");
-        _tagNameMap.put(TagFacesDetected, "Faces Detected");
-        _tagNameMap.put(TagFaceDetectArea, "Face Detect Area");
-        _tagNameMap.put(TagMaxFaces, "Max Faces");
-        _tagNameMap.put(TagFaceDetectFrameSize, "Face Detect Frame Size");
-        _tagNameMap.put(TagFaceDetectFrameCrop, "Face Detect Frame Crop");
-        _tagNameMap.put(TagCameraTemperature , "Camera Temperature");
-        _tagNameMap.put(TagKeystoneCompensation, "Keystone Compensation");
-        _tagNameMap.put(TagKeystoneDirection, "Keystone Direction");
-        _tagNameMap.put(TagKeystoneValue, "Keystone Value");
-    }
-
-    public OlympusImageProcessingMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusImageProcessingMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Image Processing";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,992 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.imaging.PhotographicConversions;
-import com.drew.lang.Rational;
-import com.drew.lang.DateUtil;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-
-import static com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link OlympusMakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusMakernoteDescriptor extends TagDescriptor<OlympusMakernoteDirectory>
-{
-    // TODO extend support for some offset-encoded byte[] tags: http://www.ozhiker.com/electronics/pjmt/jpeg_info/olympus_mn.html
-
-    public OlympusMakernoteDescriptor(@NotNull OlympusMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_MAKERNOTE_VERSION:
-                return getMakernoteVersionDescription();
-            case TAG_COLOUR_MODE:
-                return getColorModeDescription();
-            case TAG_IMAGE_QUALITY_1:
-                return getImageQuality1Description();
-            case TAG_IMAGE_QUALITY_2:
-                return getImageQuality2Description();
-            case TAG_SPECIAL_MODE:
-                return getSpecialModeDescription();
-            case TAG_JPEG_QUALITY:
-                return getJpegQualityDescription();
-            case TAG_MACRO_MODE:
-                return getMacroModeDescription();
-            case TAG_BW_MODE:
-                return getBWModeDescription();
-            case TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case TAG_FOCAL_PLANE_DIAGONAL:
-                return getFocalPlaneDiagonalDescription();
-            case TAG_CAMERA_TYPE:
-                return getCameraTypeDescription();
-            case TAG_CAMERA_ID:
-                return getCameraIdDescription();
-            case TAG_ONE_TOUCH_WB:
-                return getOneTouchWbDescription();
-            case TAG_SHUTTER_SPEED_VALUE:
-                return getShutterSpeedDescription();
-            case TAG_ISO_VALUE:
-                return getIsoValueDescription();
-            case TAG_APERTURE_VALUE:
-                return getApertureValueDescription();
-            case TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            case TAG_FOCUS_RANGE:
-                return getFocusRangeDescription();
-            case TAG_FOCUS_MODE:
-                return getFocusModeDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case TAG_COLOUR_MATRIX:
-                return getColorMatrixDescription();
-            case TAG_WB_MODE:
-                return getWbModeDescription();
-            case TAG_RED_BALANCE:
-                return getRedBalanceDescription();
-            case TAG_BLUE_BALANCE:
-                return getBlueBalanceDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_PREVIEW_IMAGE_VALID:
-                return getPreviewImageValidDescription();
-
-            case CameraSettings.TAG_EXPOSURE_MODE:
-                return getExposureModeDescription();
-            case CameraSettings.TAG_FLASH_MODE:
-                return getFlashModeCameraSettingDescription();
-            case CameraSettings.TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case CameraSettings.TAG_IMAGE_SIZE:
-                return getImageSizeDescription();
-            case CameraSettings.TAG_IMAGE_QUALITY:
-                return getImageQualityDescription();
-            case CameraSettings.TAG_SHOOTING_MODE:
-                return getShootingModeDescription();
-            case CameraSettings.TAG_METERING_MODE:
-                return getMeteringModeDescription();
-            case CameraSettings.TAG_APEX_FILM_SPEED_VALUE:
-                return getApexFilmSpeedDescription();
-            case CameraSettings.TAG_APEX_SHUTTER_SPEED_TIME_VALUE:
-                return getApexShutterSpeedTimeDescription();
-            case CameraSettings.TAG_APEX_APERTURE_VALUE:
-                return getApexApertureDescription();
-            case CameraSettings.TAG_MACRO_MODE:
-                return getMacroModeCameraSettingDescription();
-            case CameraSettings.TAG_DIGITAL_ZOOM:
-                return getDigitalZoomCameraSettingDescription();
-            case CameraSettings.TAG_EXPOSURE_COMPENSATION:
-                return getExposureCompensationDescription();
-            case CameraSettings.TAG_BRACKET_STEP:
-                return getBracketStepDescription();
-
-            case CameraSettings.TAG_INTERVAL_LENGTH:
-                return getIntervalLengthDescription();
-            case CameraSettings.TAG_INTERVAL_NUMBER:
-                return getIntervalNumberDescription();
-            case CameraSettings.TAG_FOCAL_LENGTH:
-                return getFocalLengthDescription();
-            case CameraSettings.TAG_FOCUS_DISTANCE:
-                return getFocusDistanceDescription();
-            case CameraSettings.TAG_FLASH_FIRED:
-                return getFlashFiredDescription();
-            case CameraSettings.TAG_DATE:
-                return getDateDescription();
-            case CameraSettings.TAG_TIME:
-                return getTimeDescription();
-            case CameraSettings.TAG_MAX_APERTURE_AT_FOCAL_LENGTH:
-                return getMaxApertureAtFocalLengthDescription();
-
-            case CameraSettings.TAG_FILE_NUMBER_MEMORY:
-                return getFileNumberMemoryDescription();
-            case CameraSettings.TAG_LAST_FILE_NUMBER:
-                return getLastFileNumberDescription();
-            case CameraSettings.TAG_WHITE_BALANCE_RED:
-                return getWhiteBalanceRedDescription();
-            case CameraSettings.TAG_WHITE_BALANCE_GREEN:
-                return getWhiteBalanceGreenDescription();
-            case CameraSettings.TAG_WHITE_BALANCE_BLUE:
-                return getWhiteBalanceBlueDescription();
-            case CameraSettings.TAG_SATURATION:
-                return getSaturationDescription();
-            case CameraSettings.TAG_CONTRAST:
-                return getContrastCameraSettingDescription();
-            case CameraSettings.TAG_SHARPNESS:
-                return getSharpnessCameraSettingDescription();
-            case CameraSettings.TAG_SUBJECT_PROGRAM:
-                return getSubjectProgramDescription();
-            case CameraSettings.TAG_FLASH_COMPENSATION:
-                return getFlashCompensationDescription();
-            case CameraSettings.TAG_ISO_SETTING:
-                return getIsoSettingDescription();
-            case CameraSettings.TAG_CAMERA_MODEL:
-                return getCameraModelDescription();
-            case CameraSettings.TAG_INTERVAL_MODE:
-                return getIntervalModeDescription();
-            case CameraSettings.TAG_FOLDER_NAME:
-                return getFolderNameDescription();
-            case CameraSettings.TAG_COLOR_MODE:
-                return getColorModeCameraSettingDescription();
-            case CameraSettings.TAG_COLOR_FILTER:
-                return getColorFilterDescription();
-            case CameraSettings.TAG_BLACK_AND_WHITE_FILTER:
-                return getBlackAndWhiteFilterDescription();
-            case CameraSettings.TAG_INTERNAL_FLASH:
-                return getInternalFlashDescription();
-            case CameraSettings.TAG_APEX_BRIGHTNESS_VALUE:
-                return getApexBrightnessDescription();
-            case CameraSettings.TAG_SPOT_FOCUS_POINT_X_COORDINATE:
-                return getSpotFocusPointXCoordinateDescription();
-            case CameraSettings.TAG_SPOT_FOCUS_POINT_Y_COORDINATE:
-                return getSpotFocusPointYCoordinateDescription();
-            case CameraSettings.TAG_WIDE_FOCUS_ZONE:
-                return getWideFocusZoneDescription();
-            case CameraSettings.TAG_FOCUS_MODE:
-                return getFocusModeCameraSettingDescription();
-            case CameraSettings.TAG_FOCUS_AREA:
-                return getFocusAreaDescription();
-            case CameraSettings.TAG_DEC_SWITCH_POSITION:
-                return getDecSwitchPositionDescription();
-
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getExposureModeDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_EXPOSURE_MODE, "P", "A", "S", "M");
-    }
-
-    @Nullable
-    public String getFlashModeCameraSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FLASH_MODE,
-            "Normal", "Red-eye reduction", "Rear flash sync", "Wireless");
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_WHITE_BALANCE,
-            "Auto", // 0
-            "Daylight",
-            "Cloudy",
-            "Tungsten",
-            null,
-            "Custom", // 5
-            null,
-            "Fluorescent",
-            "Fluorescent 2",
-            null,
-            null, // 10
-            "Custom 2",
-            "Custom 3"
-        );
-    }
-
-    @Nullable
-    public String getImageSizeDescription()
-    {
-        // This is a pretty weird way to store this information!
-        return getIndexedDescription(CameraSettings.TAG_IMAGE_SIZE, "2560 x 1920", "1600 x 1200", "1280 x 960", "640 x 480");
-    }
-
-    @Nullable
-    public String getImageQualityDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_IMAGE_QUALITY, "Raw", "Super Fine", "Fine", "Standard", "Economy", "Extra Fine");
-    }
-
-    @Nullable
-    public String getShootingModeDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_SHOOTING_MODE,
-            "Single",
-            "Continuous",
-            "Self Timer",
-            null,
-            "Bracketing",
-            "Interval",
-            "UHS Continuous",
-            "HS Continuous"
-        );
-    }
-
-    @Nullable
-    public String getMeteringModeDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_METERING_MODE, "Multi-Segment", "Centre Weighted", "Spot");
-    }
-
-    @Nullable
-    public String getApexFilmSpeedDescription()
-    {
-        // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html#Minolta_Camera_Settings
-        // Apex Speed value = value/8-1 ,
-        // ISO = (2^(value/8-1))*3.125
-        Long value = _directory.getLongObject(CameraSettings.TAG_APEX_FILM_SPEED_VALUE);
-
-        if (value == null)
-            return null;
-
-        double iso = Math.pow((value / 8d) - 1, 2) * 3.125;
-        DecimalFormat format = new DecimalFormat("0.##");
-        format.setRoundingMode(RoundingMode.HALF_UP);
-        return format.format(iso);
-    }
-
-    @Nullable
-    public String getApexShutterSpeedTimeDescription()
-    {
-        // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html#Minolta_Camera_Settings
-        // Apex Time value = value/8-6 ,
-        // ShutterSpeed = 2^( (48-value)/8 ),
-        // Due to rounding error value=8 should be displayed as 30 sec.
-        Long value = _directory.getLongObject(CameraSettings.TAG_APEX_SHUTTER_SPEED_TIME_VALUE);
-
-        if (value == null)
-            return null;
-
-        double shutterSpeed = Math.pow((49-value) / 8d, 2);
-        DecimalFormat format = new DecimalFormat("0.###");
-        format.setRoundingMode(RoundingMode.HALF_UP);
-        return format.format(shutterSpeed) + " sec";
-    }
-
-    @Nullable
-    public String getApexApertureDescription()
-    {
-        // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html#Minolta_Camera_Settings
-        // Apex Aperture Value = value/8-1 ,
-        // Aperture F-stop = 2^( value/16-0.5 )
-        Long value = _directory.getLongObject(CameraSettings.TAG_APEX_APERTURE_VALUE);
-
-        if (value == null)
-            return null;
-
-        double fStop = Math.pow((value/16d) - 0.5, 2);
-        return getFStopDescription(fStop);
-    }
-
-    @Nullable
-    public String getMacroModeCameraSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_MACRO_MODE, "Off", "On");
-    }
-
-    @Nullable
-    public String getDigitalZoomCameraSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_DIGITAL_ZOOM, "Off", "Electronic magnification", "Digital zoom 2x");
-    }
-
-    @Nullable
-    public String getExposureCompensationDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_EXPOSURE_COMPENSATION);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return value == null ? null : format.format((value / 3d) - 2) + " EV";
-    }
-
-    @Nullable
-    public String getBracketStepDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_BRACKET_STEP, "1/3 EV", "2/3 EV", "1 EV");
-    }
-
-    @Nullable
-    public String getIntervalLengthDescription()
-    {
-        if (!_directory.isIntervalMode())
-            return "N/A";
-
-        Long value = _directory.getLongObject(CameraSettings.TAG_INTERVAL_LENGTH);
-        return value == null ? null : value + " min";
-    }
-
-    @Nullable
-    public String getIntervalNumberDescription()
-    {
-        if (!_directory.isIntervalMode())
-            return "N/A";
-
-        Long value = _directory.getLongObject(CameraSettings.TAG_INTERVAL_NUMBER);
-        return value == null ? null : Long.toString(value);
-    }
-
-    @Nullable
-    public String getFocalLengthDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_FOCAL_LENGTH);
-        return value == null ? null : getFocalLengthDescription(value/256d);
-    }
-
-    @Nullable
-    public String getFocusDistanceDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_FOCUS_DISTANCE);
-        return value == null
-            ? null
-            : value == 0
-                ? "Infinity"
-                : value + " mm";
-    }
-
-    @Nullable
-    public String getFlashFiredDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FLASH_FIRED, "No", "Yes");
-    }
-
-    @Nullable
-    public String getDateDescription()
-    {
-        // day = value%256,
-        // month = floor( (value - floor( value/65536 )*65536 )/256 )
-        // year = floor( value/65536)
-        Long value = _directory.getLongObject(CameraSettings.TAG_DATE);
-        if (value == null)
-            return null;
-
-        int day = (int) (value & 0xFF);
-        int month = (int) ((value >> 16) & 0xFF);
-        int year = (int) ((value >> 8) & 0xFF) + 1970;
-
-        if (!DateUtil.isValidDate(year, month, day))
-            return "Invalid date";
-
-        return String.format("%04d-%02d-%02d", year, month + 1, day);
-    }
-
-    @Nullable
-    public String getTimeDescription()
-    {
-        // hours = floor( value/65536 ),
-        // minutes = floor( ( value - floor( value/65536 )*65536 )/256 ),
-        // seconds = value%256
-        Long value = _directory.getLongObject(CameraSettings.TAG_TIME);
-        if (value == null)
-            return null;
-
-        int hours = (int) ((value >> 8) & 0xFF);
-        int minutes = (int) ((value >> 16) & 0xFF);
-        int seconds = (int) (value & 0xFF);
-
-        if (!DateUtil.isValidTime(hours, minutes, seconds))
-            return "Invalid time";
-
-        return String.format("%02d:%02d:%02d", hours, minutes, seconds);
-    }
-
-    @Nullable
-    public String getMaxApertureAtFocalLengthDescription()
-    {
-        // Aperture F-Stop = 2^(value/16-0.5)
-        Long value = _directory.getLongObject(CameraSettings.TAG_TIME);
-        if (value == null)
-            return null;
-        double fStop = Math.pow((value/16d) - 0.5, 2);
-        return getFStopDescription(fStop);
-    }
-
-    @Nullable
-    public String getFileNumberMemoryDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FILE_NUMBER_MEMORY, "Off", "On");
-    }
-
-    @Nullable
-    public String getLastFileNumberDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_LAST_FILE_NUMBER);
-        return value == null
-            ? null
-            : value == 0
-                ? "File Number Memory Off"
-                : Long.toString(value);
-    }
-
-    @Nullable
-    public String getWhiteBalanceRedDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_WHITE_BALANCE_RED);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return value == null ? null : format.format(value/256d);
-    }
-
-    @Nullable
-    public String getWhiteBalanceGreenDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_WHITE_BALANCE_GREEN);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return value == null ? null : format.format(value/256d);
-    }
-
-    @Nullable
-    public String getWhiteBalanceBlueDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_WHITE_BALANCE_BLUE);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return value == null ? null : format.format(value / 256d);
-    }
-
-    @Nullable
-    public String getSaturationDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_SATURATION);
-        return value == null ? null : Long.toString(value-3);
-    }
-
-    @Nullable
-    public String getContrastCameraSettingDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_CONTRAST);
-        return value == null ? null : Long.toString(value-3);
-    }
-
-    @Nullable
-    public String getSharpnessCameraSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_SHARPNESS, "Hard", "Normal", "Soft");
-    }
-
-    @Nullable
-    public String getSubjectProgramDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_SUBJECT_PROGRAM, "None", "Portrait", "Text", "Night Portrait", "Sunset", "Sports Action");
-    }
-
-    @Nullable
-    public String getFlashCompensationDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_FLASH_COMPENSATION);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return value == null ? null : format.format((value-6)/3d) + " EV";
-    }
-
-    @Nullable
-    public String getIsoSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_ISO_SETTING, "100", "200", "400", "800", "Auto", "64");
-    }
-
-    @Nullable
-    public String getCameraModelDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_CAMERA_MODEL,
-            "DiMAGE 7",
-            "DiMAGE 5",
-            "DiMAGE S304",
-            "DiMAGE S404",
-            "DiMAGE 7i",
-            "DiMAGE 7Hi",
-            "DiMAGE A1",
-            "DiMAGE S414");
-    }
-
-    @Nullable
-    public String getIntervalModeDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_INTERVAL_MODE, "Still Image", "Time Lapse Movie");
-    }
-
-    @Nullable
-    public String getFolderNameDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FOLDER_NAME, "Standard Form", "Data Form");
-    }
-
-    @Nullable
-    public String getColorModeCameraSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_COLOR_MODE, "Natural Color", "Black & White", "Vivid Color", "Solarization", "AdobeRGB");
-    }
-
-    @Nullable
-    public String getColorFilterDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_COLOR_FILTER);
-        return value == null ? null : Long.toString(value-3);
-    }
-
-    @Nullable
-    public String getBlackAndWhiteFilterDescription()
-    {
-        return super.getDescription(CameraSettings.TAG_BLACK_AND_WHITE_FILTER);
-    }
-
-    @Nullable
-    public String getInternalFlashDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_INTERNAL_FLASH, "Did Not Fire", "Fired");
-    }
-
-    @Nullable
-    public String getApexBrightnessDescription()
-    {
-        Long value = _directory.getLongObject(CameraSettings.TAG_APEX_BRIGHTNESS_VALUE);
-        DecimalFormat format = new DecimalFormat("0.##");
-        return value == null ? null : format.format((value/8d)-6);
-    }
-
-    @Nullable
-    public String getSpotFocusPointXCoordinateDescription()
-    {
-        return super.getDescription(CameraSettings.TAG_SPOT_FOCUS_POINT_X_COORDINATE);
-    }
-
-    @Nullable
-    public String getSpotFocusPointYCoordinateDescription()
-    {
-        return super.getDescription(CameraSettings.TAG_SPOT_FOCUS_POINT_Y_COORDINATE);
-    }
-
-    @Nullable
-    public String getWideFocusZoneDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_WIDE_FOCUS_ZONE,
-            "No Zone or AF Failed",
-            "Center Zone (Horizontal Orientation)",
-            "Center Zone (Vertical Orientation)",
-            "Left Zone",
-            "Right Zone"
-        );
-    }
-
-    @Nullable
-    public String getFocusModeCameraSettingDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FOCUS_MODE, "Auto Focus", "Manual Focus");
-    }
-
-    @Nullable
-    public String getFocusAreaDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_FOCUS_AREA, "Wide Focus (Normal)", "Spot Focus");
-    }
-
-    @Nullable
-    public String getDecSwitchPositionDescription()
-    {
-        return getIndexedDescription(CameraSettings.TAG_DEC_SWITCH_POSITION, "Exposure", "Contrast", "Saturation", "Filter");
-    }
-
-    @Nullable
-    public String getMakernoteVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_MAKERNOTE_VERSION, 2);
-    }
-
-    @Nullable
-    public String getImageQuality2Description()
-    {
-        return getIndexedDescription(TAG_IMAGE_QUALITY_2,
-            "Raw",
-            "Super Fine",
-            "Fine",
-            "Standard",
-            "Extra Fine");
-    }
-
-    @Nullable
-    public String getImageQuality1Description()
-    {
-        return getIndexedDescription(TAG_IMAGE_QUALITY_1,
-            "Raw",
-            "Super Fine",
-            "Fine",
-            "Standard",
-            "Extra Fine");
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        return getIndexedDescription(TAG_COLOUR_MODE,
-            "Natural Colour",
-            "Black & White",
-            "Vivid Colour",
-            "Solarization",
-            "AdobeRGB");
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        return getIndexedDescription(TAG_SHARPNESS, "Normal", "Hard", "Soft");
-    }
-
-    @Nullable
-    public String getColorMatrixDescription()
-    {
-        int[] obj = _directory.getIntArray(TAG_COLOUR_MATRIX);
-        if (obj == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < obj.length; i++) {
-            sb.append((short)obj[i]);
-            if (i < obj.length - 1)
-                sb.append(" ");
-        }
-        return sb.length() == 0 ? null : sb.toString();
-    }
-
-    @Nullable
-    public String getWbModeDescription()
-    {
-        int[] obj = _directory.getIntArray(TAG_WB_MODE);
-        if (obj == null)
-            return null;
-
-        String val = String.format("%d %d", obj[0], obj[1]);
-
-        if(val.equals("1 0"))
-            return "Auto";
-        else if(val.equals("1 2"))
-            return "Auto (2)";
-        else if(val.equals("1 4"))
-            return "Auto (4)";
-        else if(val.equals("2 2"))
-            return "3000 Kelvin";
-        else if(val.equals("2 3"))
-            return "3700 Kelvin";
-        else if(val.equals("2 4"))
-            return "4000 Kelvin";
-        else if(val.equals("2 5"))
-            return "4500 Kelvin";
-        else if(val.equals("2 6"))
-            return "5500 Kelvin";
-        else if(val.equals("2 7"))
-            return "6500 Kelvin";
-        else if(val.equals("2 8"))
-            return "7500 Kelvin";
-        else if(val.equals("3 0"))
-            return "One-touch";
-        else
-            return "Unknown " + val;
-    }
-
-    @Nullable
-    public String getRedBalanceDescription()
-    {
-        int[] values = _directory.getIntArray(TAG_RED_BALANCE);
-        if (values == null)
-            return null;
-
-        short value = (short)values[0];
-
-        return String.valueOf((double)value/256d);
-    }
-
-    @Nullable
-    public String getBlueBalanceDescription()
-    {
-        int[] values = _directory.getIntArray(TAG_BLUE_BALANCE);
-        if (values == null)
-            return null;
-
-        short value = (short)values[0];
-
-        return String.valueOf((double)value/256d);
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        return getIndexedDescription(TAG_CONTRAST, "High", "Normal", "Low");
-    }
-
-    @Nullable
-    public String getPreviewImageValidDescription()
-    {
-        return getIndexedDescription(TAG_PREVIEW_IMAGE_VALID, "No", "Yes");
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE, "Auto", "Manual");
-    }
-
-    @Nullable
-    public String getFocusRangeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_RANGE, "Normal", "Macro");
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_MODE, null, null, "On", "Off");
-    }
-
-    @Nullable
-    public String getDigitalZoomDescription()
-    {
-        Rational value = _directory.getRational(TAG_DIGITAL_ZOOM);
-        if (value == null)
-            return null;
-        return value.toSimpleString(false);
-    }
-
-    @Nullable
-    public String getFocalPlaneDiagonalDescription()
-    {
-        Rational value = _directory.getRational(TAG_FOCAL_PLANE_DIAGONAL);
-        if (value == null)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("0.###");
-        return format.format(value.doubleValue()) + " mm";
-    }
-
-    @Nullable
-    public String getCameraTypeDescription()
-    {
-        String cameratype = _directory.getString(TAG_CAMERA_TYPE);
-        if(cameratype == null)
-            return null;
-
-        if(OlympusMakernoteDirectory.OlympusCameraTypes.containsKey(cameratype))
-            return OlympusMakernoteDirectory.OlympusCameraTypes.get(cameratype);
-
-        return cameratype;
-    }
-
-    @Nullable
-    public String getCameraIdDescription()
-    {
-        byte[] bytes = _directory.getByteArray(TAG_CAMERA_ID);
-        if (bytes == null)
-            return null;
-        return new String(bytes);
-    }
-
-    @Nullable
-    public String getOneTouchWbDescription()
-    {
-        return getIndexedDescription(TAG_ONE_TOUCH_WB, "Off", "On", "On (Preset)");
-    }
-
-    @Nullable
-    public String getShutterSpeedDescription()
-    {
-        return super.getShutterSpeedDescription(TAG_SHUTTER_SPEED_VALUE);
-    }
-
-    @Nullable
-    public String getIsoValueDescription()
-    {
-        Rational value = _directory.getRational(TAG_ISO_VALUE);
-        if (value == null)
-            return null;
-
-        return String.valueOf(Math.round(Math.pow(2, value.doubleValue() - 5) * 100));
-    }
-
-    @Nullable
-    public String getApertureValueDescription()
-    {
-        Double aperture = _directory.getDoubleObject(TAG_APERTURE_VALUE);
-        if (aperture == null)
-            return null;
-        double fStop = PhotographicConversions.apertureToFStop(aperture);
-        return getFStopDescription(fStop);
-    }
-
-    @Nullable
-    public String getMacroModeDescription()
-    {
-        return getIndexedDescription(TAG_MACRO_MODE, "Normal (no macro)", "Macro");
-    }
-
-    @Nullable
-    public String getBWModeDescription()
-    {
-        return getIndexedDescription(TAG_BW_MODE, "Off", "On");
-    }
-
-    @Nullable
-    public String getJpegQualityDescription()
-    {
-        String cameratype = _directory.getString(TAG_CAMERA_TYPE);
-
-        if(cameratype != null)
-        {
-            Integer value = _directory.getInteger(TAG_JPEG_QUALITY);
-            if(value == null)
-                return null;
-
-            if((cameratype.startsWith("SX") && !cameratype.startsWith("SX151"))
-                || cameratype.startsWith("D4322"))
-            {
-                switch (value)
-                {
-                    case 0:
-                        return "Standard Quality (Low)";
-                    case 1:
-                        return "High Quality (Normal)";
-                    case 2:
-                        return "Super High Quality (Fine)";
-                    case 6:
-                        return "RAW";
-                    default:
-                        return "Unknown (" + value.toString() + ")";
-                }
-            }
-            else
-            {
-                switch (value)
-                {
-                    case 0:
-                        return "Standard Quality (Low)";
-                    case 1:
-                        return "High Quality (Normal)";
-                    case 2:
-                        return "Super High Quality (Fine)";
-                    case 4:
-                        return "RAW";
-                    case 5:
-                        return "Medium-Fine";
-                    case 6:
-                        return "Small-Fine";
-                    case 33:
-                        return "Uncompressed";
-                    default:
-                        return "Unknown (" + value.toString() + ")";
-                }
-            }
-        }
-        else
-            return getIndexedDescription(TAG_JPEG_QUALITY,
-            1,
-            "Standard Quality",
-            "High Quality",
-            "Super High Quality");
-    }
-
-    @Nullable
-    public String getSpecialModeDescription()
-    {
-        long[] values = (long[])_directory.getObject(TAG_SPECIAL_MODE);
-        if (values==null)
-            return null;
-        if (values.length < 1)
-            return "";
-        StringBuilder desc = new StringBuilder();
-
-        switch ((int)values[0]) {
-            case 0:
-                desc.append("Normal picture taking mode");
-                break;
-            case 1:
-                desc.append("Unknown picture taking mode");
-                break;
-            case 2:
-                desc.append("Fast picture taking mode");
-                break;
-            case 3:
-                desc.append("Panorama picture taking mode");
-                break;
-            default:
-                desc.append("Unknown picture taking mode");
-                break;
-        }
-
-        if (values.length >= 2) {
-            switch ((int)values[1]) {
-                case 0:
-                    break;
-                case 1:
-                    desc.append(" / 1st in a sequence");
-                    break;
-                case 2:
-                    desc.append(" / 2nd in a sequence");
-                    break;
-                case 3:
-                    desc.append(" / 3rd in a sequence");
-                    break;
-                default:
-                    desc.append(" / ");
-                    desc.append(values[1]);
-                    desc.append("th in a sequence");
-                    break;
-            }
-        }
-        if (values.length >= 3) {
-            switch ((int)values[2]) {
-                case 1:
-                    desc.append(" / Left to right panorama direction");
-                    break;
-                case 2:
-                    desc.append(" / Right to left panorama direction");
-                    break;
-                case 3:
-                    desc.append(" / Bottom to top panorama direction");
-                    break;
-                case 4:
-                    desc.append(" / Top to bottom panorama direction");
-                    break;
-            }
-        }
-
-        return desc.toString();
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,837 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-/**
- * The Olympus makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusMakernoteDirectory extends Directory
-{
-    /** Used by Konica / Minolta cameras. */
-    public static final int TAG_MAKERNOTE_VERSION = 0x0000;
-    /** Used by Konica / Minolta cameras. */
-    public static final int TAG_CAMERA_SETTINGS_1 = 0x0001;
-    /** Alternate Camera Settings Tag. Used by Konica / Minolta cameras. */
-    public static final int TAG_CAMERA_SETTINGS_2 = 0x0003;
-    /** Used by Konica / Minolta cameras. */
-    public static final int TAG_COMPRESSED_IMAGE_SIZE = 0x0040;
-    /** Used by Konica / Minolta cameras. */
-    public static final int TAG_MINOLTA_THUMBNAIL_OFFSET_1 = 0x0081;
-    /** Alternate Thumbnail Offset. Used by Konica / Minolta cameras. */
-    public static final int TAG_MINOLTA_THUMBNAIL_OFFSET_2 = 0x0088;
-    /** Length of thumbnail in bytes. Used by Konica / Minolta cameras. */
-    public static final int TAG_MINOLTA_THUMBNAIL_LENGTH = 0x0089;
-
-    public static final int TAG_THUMBNAIL_IMAGE = 0x0100;
-
-    /**
-     * Used by Konica / Minolta cameras
-     * 0 = Natural Colour
-     * 1 = Black &amp; White
-     * 2 = Vivid colour
-     * 3 = Solarization
-     * 4 = AdobeRGB
-     */
-    public static final int TAG_COLOUR_MODE = 0x0101;
-
-    /**
-     * Used by Konica / Minolta cameras.
-     * 0 = Raw
-     * 1 = Super Fine
-     * 2 = Fine
-     * 3 = Standard
-     * 4 = Extra Fine
-     */
-    public static final int TAG_IMAGE_QUALITY_1 = 0x0102;
-
-    /**
-     * Not 100% sure about this tag.
-     * <p>
-     * Used by Konica / Minolta cameras.
-     * 0 = Raw
-     * 1 = Super Fine
-     * 2 = Fine
-     * 3 = Standard
-     * 4 = Extra Fine
-     */
-    public static final int TAG_IMAGE_QUALITY_2 = 0x0103;
-
-    public static final int TAG_BODY_FIRMWARE_VERSION = 0x0104;
-
-    /**
-     * Three values:
-     * Value 1: 0=Normal, 2=Fast, 3=Panorama
-     * Value 2: Sequence Number Value 3:
-     * 1 = Panorama Direction: Left to Right
-     * 2 = Panorama Direction: Right to Left
-     * 3 = Panorama Direction: Bottom to Top
-     * 4 = Panorama Direction: Top to Bottom
-     */
-    public static final int TAG_SPECIAL_MODE = 0x0200;
-
-    /**
-     * 1 = Standard Quality
-     * 2 = High Quality
-     * 3 = Super High Quality
-     */
-    public static final int TAG_JPEG_QUALITY = 0x0201;
-
-    /**
-     * 0 = Normal (Not Macro)
-     * 1 = Macro
-     */
-    public static final int TAG_MACRO_MODE = 0x0202;
-
-    /**
-     * 0 = Off, 1 = On
-     */
-    public static final int TAG_BW_MODE = 0x0203;
-
-    /** Zoom Factor (0 or 1 = normal) */
-    public static final int TAG_DIGITAL_ZOOM = 0x0204;
-    public static final int TAG_FOCAL_PLANE_DIAGONAL = 0x0205;
-    public static final int TAG_LENS_DISTORTION_PARAMETERS = 0x0206;
-    public static final int TAG_CAMERA_TYPE = 0x0207;
-    public static final int TAG_PICT_INFO = 0x0208;
-    public static final int TAG_CAMERA_ID = 0x0209;
-
-    /**
-     * Used by Epson cameras
-     * Units = pixels
-     */
-    public static final int TAG_IMAGE_WIDTH = 0x020B;
-
-    /**
-     * Used by Epson cameras
-     * Units = pixels
-     */
-    public static final int TAG_IMAGE_HEIGHT = 0x020C;
-
-    /** A string. Used by Epson cameras. */
-    public static final int TAG_ORIGINAL_MANUFACTURER_MODEL = 0x020D;
-
-    public static final int TAG_PREVIEW_IMAGE = 0x0280;
-    public static final int TAG_PRE_CAPTURE_FRAMES = 0x0300;
-    public static final int TAG_WHITE_BOARD = 0x0301;
-    public static final int TAG_ONE_TOUCH_WB = 0x0302;
-    public static final int TAG_WHITE_BALANCE_BRACKET = 0x0303;
-    public static final int TAG_WHITE_BALANCE_BIAS = 0x0304;
-    public static final int TAG_SCENE_MODE = 0x0403;
-    public static final int TAG_SERIAL_NUMBER_1 = 0x0404;
-    public static final int TAG_FIRMWARE = 0x0405;
-
-    /**
-     * See the PIM specification here:
-     * http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
-     */
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    public static final int TAG_DATA_DUMP_1 = 0x0F00;
-    public static final int TAG_DATA_DUMP_2 = 0x0F01;
-
-    public static final int TAG_SHUTTER_SPEED_VALUE = 0x1000;
-    public static final int TAG_ISO_VALUE = 0x1001;
-    public static final int TAG_APERTURE_VALUE = 0x1002;
-    public static final int TAG_BRIGHTNESS_VALUE = 0x1003;
-    public static final int TAG_FLASH_MODE = 0x1004;
-    public static final int TAG_FLASH_DEVICE = 0x1005;
-    public static final int TAG_BRACKET = 0x1006;
-    public static final int TAG_SENSOR_TEMPERATURE = 0x1007;
-    public static final int TAG_LENS_TEMPERATURE = 0x1008;
-    public static final int TAG_LIGHT_CONDITION = 0x1009;
-    public static final int TAG_FOCUS_RANGE = 0x100A;
-    public static final int TAG_FOCUS_MODE = 0x100B;
-    public static final int TAG_FOCUS_DISTANCE = 0x100C;
-    public static final int TAG_ZOOM = 0x100D;
-    public static final int TAG_MACRO_FOCUS = 0x100E;
-    public static final int TAG_SHARPNESS = 0x100F;
-    public static final int TAG_FLASH_CHARGE_LEVEL = 0x1010;
-    public static final int TAG_COLOUR_MATRIX = 0x1011;
-    public static final int TAG_BLACK_LEVEL = 0x1012;
-    public static final int TAG_COLOR_TEMPERATURE_BG = 0x1013;
-    public static final int TAG_COLOR_TEMPERATURE_RG = 0x1014;
-    public static final int TAG_WB_MODE = 0x1015;
-//    public static final int TAG_ = 0x1016;
-    public static final int TAG_RED_BALANCE = 0x1017;
-    public static final int TAG_BLUE_BALANCE = 0x1018;
-    public static final int TAG_COLOR_MATRIX_NUMBER = 0x1019;
-    public static final int TAG_SERIAL_NUMBER_2 = 0x101A;
-
-    public static final int TAG_EXTERNAL_FLASH_AE1_0 = 0x101B;
-    public static final int TAG_EXTERNAL_FLASH_AE2_0 = 0x101C;
-    public static final int TAG_INTERNAL_FLASH_AE1_0 = 0x101D;
-    public static final int TAG_INTERNAL_FLASH_AE2_0 = 0x101E;
-    public static final int TAG_EXTERNAL_FLASH_AE1 = 0x101F;
-    public static final int TAG_EXTERNAL_FLASH_AE2 = 0x1020;
-    public static final int TAG_INTERNAL_FLASH_AE1 = 0x1021;
-    public static final int TAG_INTERNAL_FLASH_AE2 = 0x1022;
-
-    public static final int TAG_FLASH_BIAS = 0x1023;
-    public static final int TAG_INTERNAL_FLASH_TABLE = 0x1024;
-    public static final int TAG_EXTERNAL_FLASH_G_VALUE = 0x1025;
-    public static final int TAG_EXTERNAL_FLASH_BOUNCE = 0x1026;
-    public static final int TAG_EXTERNAL_FLASH_ZOOM = 0x1027;
-    public static final int TAG_EXTERNAL_FLASH_MODE = 0x1028;
-    public static final int TAG_CONTRAST = 0x1029;
-    public static final int TAG_SHARPNESS_FACTOR = 0x102A;
-    public static final int TAG_COLOUR_CONTROL = 0x102B;
-    public static final int TAG_VALID_BITS = 0x102C;
-    public static final int TAG_CORING_FILTER = 0x102D;
-    public static final int TAG_OLYMPUS_IMAGE_WIDTH = 0x102E;
-    public static final int TAG_OLYMPUS_IMAGE_HEIGHT = 0x102F;
-    public static final int TAG_SCENE_DETECT = 0x1030;
-    public static final int TAG_SCENE_AREA = 0x1031;
-//    public static final int TAG_ = 0x1032;
-    public static final int TAG_SCENE_DETECT_DATA = 0x1033;
-    public static final int TAG_COMPRESSION_RATIO = 0x1034;
-    public static final int TAG_PREVIEW_IMAGE_VALID = 0x1035;
-    public static final int TAG_PREVIEW_IMAGE_START = 0x1036;
-    public static final int TAG_PREVIEW_IMAGE_LENGTH = 0x1037;
-    public static final int TAG_AF_RESULT = 0x1038;
-    public static final int TAG_CCD_SCAN_MODE = 0x1039;
-    public static final int TAG_NOISE_REDUCTION = 0x103A;
-    public static final int TAG_INFINITY_LENS_STEP = 0x103B;
-    public static final int TAG_NEAR_LENS_STEP = 0x103C;
-    public static final int TAG_LIGHT_VALUE_CENTER = 0x103D;
-    public static final int TAG_LIGHT_VALUE_PERIPHERY = 0x103E;
-    public static final int TAG_FIELD_COUNT = 0x103F;
-    public static final int TAG_EQUIPMENT = 0x2010;
-    public static final int TAG_CAMERA_SETTINGS = 0x2020;
-    public static final int TAG_RAW_DEVELOPMENT = 0x2030;
-    public static final int TAG_RAW_DEVELOPMENT_2 = 0x2031;
-    public static final int TAG_IMAGE_PROCESSING = 0x2040;
-    public static final int TAG_FOCUS_INFO = 0x2050;
-    public static final int TAG_RAW_INFO = 0x3000;
-    public static final int TAG_MAIN_INFO = 0x4000;
-
-    public final static class CameraSettings
-    {
-        // These 'sub'-tag values have been created for consistency -- they don't exist within the Makernote IFD
-        private static final int OFFSET = 0xF000;
-
-        public static final int TAG_EXPOSURE_MODE = OFFSET + 2;
-        public static final int TAG_FLASH_MODE = OFFSET + 3;
-        public static final int TAG_WHITE_BALANCE = OFFSET + 4;
-        public static final int TAG_IMAGE_SIZE = OFFSET + 5;
-        public static final int TAG_IMAGE_QUALITY = OFFSET + 6;
-        public static final int TAG_SHOOTING_MODE = OFFSET + 7;
-        public static final int TAG_METERING_MODE = OFFSET + 8;
-        public static final int TAG_APEX_FILM_SPEED_VALUE = OFFSET + 9;
-        public static final int TAG_APEX_SHUTTER_SPEED_TIME_VALUE = OFFSET + 10;
-        public static final int TAG_APEX_APERTURE_VALUE = OFFSET + 11;
-        public static final int TAG_MACRO_MODE = OFFSET + 12;
-        public static final int TAG_DIGITAL_ZOOM = OFFSET + 13;
-        public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 14;
-        public static final int TAG_BRACKET_STEP = OFFSET + 15;
-        // 16 missing
-        public static final int TAG_INTERVAL_LENGTH = OFFSET + 17;
-        public static final int TAG_INTERVAL_NUMBER = OFFSET + 18;
-        public static final int TAG_FOCAL_LENGTH = OFFSET + 19;
-        public static final int TAG_FOCUS_DISTANCE = OFFSET + 20;
-        public static final int TAG_FLASH_FIRED = OFFSET + 21;
-        public static final int TAG_DATE = OFFSET + 22;
-        public static final int TAG_TIME = OFFSET + 23;
-        public static final int TAG_MAX_APERTURE_AT_FOCAL_LENGTH = OFFSET + 24;
-        // 25, 26 missing
-        public static final int TAG_FILE_NUMBER_MEMORY = OFFSET + 27;
-        public static final int TAG_LAST_FILE_NUMBER = OFFSET + 28;
-        public static final int TAG_WHITE_BALANCE_RED = OFFSET + 29;
-        public static final int TAG_WHITE_BALANCE_GREEN = OFFSET + 30;
-        public static final int TAG_WHITE_BALANCE_BLUE = OFFSET + 31;
-        public static final int TAG_SATURATION = OFFSET + 32;
-        public static final int TAG_CONTRAST = OFFSET + 33;
-        public static final int TAG_SHARPNESS = OFFSET + 34;
-        public static final int TAG_SUBJECT_PROGRAM = OFFSET + 35;
-        public static final int TAG_FLASH_COMPENSATION = OFFSET + 36;
-        public static final int TAG_ISO_SETTING = OFFSET + 37;
-        public static final int TAG_CAMERA_MODEL = OFFSET + 38;
-        public static final int TAG_INTERVAL_MODE = OFFSET + 39;
-        public static final int TAG_FOLDER_NAME = OFFSET + 40;
-        public static final int TAG_COLOR_MODE = OFFSET + 41;
-        public static final int TAG_COLOR_FILTER = OFFSET + 42;
-        public static final int TAG_BLACK_AND_WHITE_FILTER = OFFSET + 43;
-        public static final int TAG_INTERNAL_FLASH = OFFSET + 44;
-        public static final int TAG_APEX_BRIGHTNESS_VALUE = OFFSET + 45;
-        public static final int TAG_SPOT_FOCUS_POINT_X_COORDINATE = OFFSET + 46;
-        public static final int TAG_SPOT_FOCUS_POINT_Y_COORDINATE = OFFSET + 47;
-        public static final int TAG_WIDE_FOCUS_ZONE = OFFSET + 48;
-        public static final int TAG_FOCUS_MODE = OFFSET + 49;
-        public static final int TAG_FOCUS_AREA = OFFSET + 50;
-        public static final int TAG_DEC_SWITCH_POSITION = OFFSET + 51;
-    }
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
-        _tagNameMap.put(TAG_CAMERA_SETTINGS_1, "Camera Settings");
-        _tagNameMap.put(TAG_CAMERA_SETTINGS_2, "Camera Settings");
-        _tagNameMap.put(TAG_COMPRESSED_IMAGE_SIZE, "Compressed Image Size");
-        _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_OFFSET_1, "Thumbnail Offset");
-        _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_OFFSET_2, "Thumbnail Offset");
-        _tagNameMap.put(TAG_MINOLTA_THUMBNAIL_LENGTH, "Thumbnail Length");
-        _tagNameMap.put(TAG_THUMBNAIL_IMAGE, "Thumbnail Image");
-        _tagNameMap.put(TAG_COLOUR_MODE, "Colour Mode");
-        _tagNameMap.put(TAG_IMAGE_QUALITY_1, "Image Quality");
-        _tagNameMap.put(TAG_IMAGE_QUALITY_2, "Image Quality");
-        _tagNameMap.put(TAG_BODY_FIRMWARE_VERSION, "Body Firmware Version");
-        _tagNameMap.put(TAG_SPECIAL_MODE, "Special Mode");
-        _tagNameMap.put(TAG_JPEG_QUALITY, "JPEG Quality");
-        _tagNameMap.put(TAG_MACRO_MODE, "Macro");
-        _tagNameMap.put(TAG_BW_MODE, "BW Mode");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_FOCAL_PLANE_DIAGONAL, "Focal Plane Diagonal");
-        _tagNameMap.put(TAG_LENS_DISTORTION_PARAMETERS, "Lens Distortion Parameters");
-        _tagNameMap.put(TAG_CAMERA_TYPE, "Camera Type");
-        _tagNameMap.put(TAG_PICT_INFO, "Pict Info");
-        _tagNameMap.put(TAG_CAMERA_ID, "Camera Id");
-        _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
-        _tagNameMap.put(TAG_IMAGE_HEIGHT, "Image Height");
-        _tagNameMap.put(TAG_ORIGINAL_MANUFACTURER_MODEL, "Original Manufacturer Model");
-        _tagNameMap.put(TAG_PREVIEW_IMAGE, "Preview Image");
-        _tagNameMap.put(TAG_PRE_CAPTURE_FRAMES, "Pre Capture Frames");
-        _tagNameMap.put(TAG_WHITE_BOARD, "White Board");
-        _tagNameMap.put(TAG_ONE_TOUCH_WB, "One Touch WB");
-        _tagNameMap.put(TAG_WHITE_BALANCE_BRACKET, "White Balance Bracket");
-        _tagNameMap.put(TAG_WHITE_BALANCE_BIAS, "White Balance Bias");
-        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
-        _tagNameMap.put(TAG_SERIAL_NUMBER_1, "Serial Number");
-        _tagNameMap.put(TAG_FIRMWARE, "Firmware");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-        _tagNameMap.put(TAG_DATA_DUMP_1, "Data Dump");
-        _tagNameMap.put(TAG_DATA_DUMP_2, "Data Dump 2");
-        _tagNameMap.put(TAG_SHUTTER_SPEED_VALUE, "Shutter Speed Value");
-        _tagNameMap.put(TAG_ISO_VALUE, "ISO Value");
-        _tagNameMap.put(TAG_APERTURE_VALUE, "Aperture Value");
-        _tagNameMap.put(TAG_BRIGHTNESS_VALUE, "Brightness Value");
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(TAG_FLASH_DEVICE, "Flash Device");
-        _tagNameMap.put(TAG_BRACKET, "Bracket");
-        _tagNameMap.put(TAG_SENSOR_TEMPERATURE, "Sensor Temperature");
-        _tagNameMap.put(TAG_LENS_TEMPERATURE, "Lens Temperature");
-        _tagNameMap.put(TAG_LIGHT_CONDITION, "Light Condition");
-        _tagNameMap.put(TAG_FOCUS_RANGE, "Focus Range");
-        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(TAG_FOCUS_DISTANCE, "Focus Distance");
-        _tagNameMap.put(TAG_ZOOM, "Zoom");
-        _tagNameMap.put(TAG_MACRO_FOCUS, "Macro Focus");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_FLASH_CHARGE_LEVEL, "Flash Charge Level");
-        _tagNameMap.put(TAG_COLOUR_MATRIX, "Colour Matrix");
-        _tagNameMap.put(TAG_BLACK_LEVEL, "Black Level");
-        _tagNameMap.put(TAG_COLOR_TEMPERATURE_BG, "Color Temperature BG");
-        _tagNameMap.put(TAG_COLOR_TEMPERATURE_RG, "Color Temperature RG");
-        _tagNameMap.put(TAG_WB_MODE, "White Balance Mode");
-        _tagNameMap.put(TAG_RED_BALANCE, "Red Balance");
-        _tagNameMap.put(TAG_BLUE_BALANCE, "Blue Balance");
-        _tagNameMap.put(TAG_COLOR_MATRIX_NUMBER, "Color Matrix Number");
-        _tagNameMap.put(TAG_SERIAL_NUMBER_2, "Serial Number");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_AE1_0, "External Flash AE1 0");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_AE2_0, "External Flash AE2 0");
-        _tagNameMap.put(TAG_INTERNAL_FLASH_AE1_0, "Internal Flash AE1 0");
-        _tagNameMap.put(TAG_INTERNAL_FLASH_AE2_0, "Internal Flash AE2 0");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_AE1, "External Flash AE1");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_AE2, "External Flash AE2");
-        _tagNameMap.put(TAG_INTERNAL_FLASH_AE1, "Internal Flash AE1");
-        _tagNameMap.put(TAG_INTERNAL_FLASH_AE2, "Internal Flash AE2");
-        _tagNameMap.put(TAG_FLASH_BIAS, "Flash Bias");
-        _tagNameMap.put(TAG_INTERNAL_FLASH_TABLE, "Internal Flash Table");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_G_VALUE, "External Flash G Value");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_BOUNCE, "External Flash Bounce");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_ZOOM, "External Flash Zoom");
-        _tagNameMap.put(TAG_EXTERNAL_FLASH_MODE, "External Flash Mode");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_SHARPNESS_FACTOR, "Sharpness Factor");
-        _tagNameMap.put(TAG_COLOUR_CONTROL, "Colour Control");
-        _tagNameMap.put(TAG_VALID_BITS, "Valid Bits");
-        _tagNameMap.put(TAG_CORING_FILTER, "Coring Filter");
-        _tagNameMap.put(TAG_OLYMPUS_IMAGE_WIDTH, "Olympus Image Width");
-        _tagNameMap.put(TAG_OLYMPUS_IMAGE_HEIGHT, "Olympus Image Height");
-        _tagNameMap.put(TAG_SCENE_DETECT, "Scene Detect");
-        _tagNameMap.put(TAG_SCENE_AREA, "Scene Area");
-        _tagNameMap.put(TAG_SCENE_DETECT_DATA, "Scene Detect Data");
-        _tagNameMap.put(TAG_COMPRESSION_RATIO, "Compression Ratio");
-        _tagNameMap.put(TAG_PREVIEW_IMAGE_VALID, "Preview Image Valid");
-        _tagNameMap.put(TAG_PREVIEW_IMAGE_START, "Preview Image Start");
-        _tagNameMap.put(TAG_PREVIEW_IMAGE_LENGTH, "Preview Image Length");
-        _tagNameMap.put(TAG_AF_RESULT, "AF Result");
-        _tagNameMap.put(TAG_CCD_SCAN_MODE, "CCD Scan Mode");
-        _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction");
-        _tagNameMap.put(TAG_INFINITY_LENS_STEP, "Infinity Lens Step");
-        _tagNameMap.put(TAG_NEAR_LENS_STEP, "Near Lens Step");
-        _tagNameMap.put(TAG_LIGHT_VALUE_CENTER, "Light Value Center");
-        _tagNameMap.put(TAG_LIGHT_VALUE_PERIPHERY, "Light Value Periphery");
-        _tagNameMap.put(TAG_FIELD_COUNT, "Field Count");
-        _tagNameMap.put(TAG_EQUIPMENT, "Equipment");
-        _tagNameMap.put(TAG_CAMERA_SETTINGS, "Camera Settings");
-        _tagNameMap.put(TAG_RAW_DEVELOPMENT, "Raw Development");
-        _tagNameMap.put(TAG_RAW_DEVELOPMENT_2, "Raw Development 2");
-        _tagNameMap.put(TAG_IMAGE_PROCESSING, "Image Processing");
-        _tagNameMap.put(TAG_FOCUS_INFO, "Focus Info");
-        _tagNameMap.put(TAG_RAW_INFO, "Raw Info");
-        _tagNameMap.put(TAG_MAIN_INFO, "Main Info");
-
-        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode");
-        _tagNameMap.put(CameraSettings.TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(CameraSettings.TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(CameraSettings.TAG_IMAGE_SIZE, "Image Size");
-        _tagNameMap.put(CameraSettings.TAG_IMAGE_QUALITY, "Image Quality");
-        _tagNameMap.put(CameraSettings.TAG_SHOOTING_MODE, "Shooting Mode");
-        _tagNameMap.put(CameraSettings.TAG_METERING_MODE, "Metering Mode");
-        _tagNameMap.put(CameraSettings.TAG_APEX_FILM_SPEED_VALUE, "Apex Film Speed Value");
-        _tagNameMap.put(CameraSettings.TAG_APEX_SHUTTER_SPEED_TIME_VALUE, "Apex Shutter Speed Time Value");
-        _tagNameMap.put(CameraSettings.TAG_APEX_APERTURE_VALUE, "Apex Aperture Value");
-        _tagNameMap.put(CameraSettings.TAG_MACRO_MODE, "Macro Mode");
-        _tagNameMap.put(CameraSettings.TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(CameraSettings.TAG_EXPOSURE_COMPENSATION, "Exposure Compensation");
-        _tagNameMap.put(CameraSettings.TAG_BRACKET_STEP, "Bracket Step");
-
-        _tagNameMap.put(CameraSettings.TAG_INTERVAL_LENGTH, "Interval Length");
-        _tagNameMap.put(CameraSettings.TAG_INTERVAL_NUMBER, "Interval Number");
-        _tagNameMap.put(CameraSettings.TAG_FOCAL_LENGTH, "Focal Length");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_DISTANCE, "Focus Distance");
-        _tagNameMap.put(CameraSettings.TAG_FLASH_FIRED, "Flash Fired");
-        _tagNameMap.put(CameraSettings.TAG_DATE, "Date");
-        _tagNameMap.put(CameraSettings.TAG_TIME, "Time");
-        _tagNameMap.put(CameraSettings.TAG_MAX_APERTURE_AT_FOCAL_LENGTH, "Max Aperture at Focal Length");
-
-        _tagNameMap.put(CameraSettings.TAG_FILE_NUMBER_MEMORY, "File Number Memory");
-        _tagNameMap.put(CameraSettings.TAG_LAST_FILE_NUMBER, "Last File Number");
-        _tagNameMap.put(CameraSettings.TAG_WHITE_BALANCE_RED, "White Balance Red");
-        _tagNameMap.put(CameraSettings.TAG_WHITE_BALANCE_GREEN, "White Balance Green");
-        _tagNameMap.put(CameraSettings.TAG_WHITE_BALANCE_BLUE, "White Balance Blue");
-        _tagNameMap.put(CameraSettings.TAG_SATURATION, "Saturation");
-        _tagNameMap.put(CameraSettings.TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(CameraSettings.TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(CameraSettings.TAG_SUBJECT_PROGRAM, "Subject Program");
-        _tagNameMap.put(CameraSettings.TAG_FLASH_COMPENSATION, "Flash Compensation");
-        _tagNameMap.put(CameraSettings.TAG_ISO_SETTING, "ISO Setting");
-        _tagNameMap.put(CameraSettings.TAG_CAMERA_MODEL, "Camera Model");
-        _tagNameMap.put(CameraSettings.TAG_INTERVAL_MODE, "Interval Mode");
-        _tagNameMap.put(CameraSettings.TAG_FOLDER_NAME, "Folder Name");
-        _tagNameMap.put(CameraSettings.TAG_COLOR_MODE, "Color Mode");
-        _tagNameMap.put(CameraSettings.TAG_COLOR_FILTER, "Color Filter");
-        _tagNameMap.put(CameraSettings.TAG_BLACK_AND_WHITE_FILTER, "Black and White Filter");
-        _tagNameMap.put(CameraSettings.TAG_INTERNAL_FLASH, "Internal Flash");
-        _tagNameMap.put(CameraSettings.TAG_APEX_BRIGHTNESS_VALUE, "Apex Brightness Value");
-        _tagNameMap.put(CameraSettings.TAG_SPOT_FOCUS_POINT_X_COORDINATE, "Spot Focus Point X Coordinate");
-        _tagNameMap.put(CameraSettings.TAG_SPOT_FOCUS_POINT_Y_COORDINATE, "Spot Focus Point Y Coordinate");
-        _tagNameMap.put(CameraSettings.TAG_WIDE_FOCUS_ZONE, "Wide Focus Zone");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(CameraSettings.TAG_FOCUS_AREA, "Focus Area");
-        _tagNameMap.put(CameraSettings.TAG_DEC_SWITCH_POSITION, "DEC Switch Position");
-    }
-
-    public OlympusMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Makernote";
-    }
-
-    @Override
-    public void setByteArray(int tagType, @NotNull byte[] bytes)
-    {
-        if (tagType == TAG_CAMERA_SETTINGS_1 || tagType == TAG_CAMERA_SETTINGS_2) {
-            processCameraSettings(bytes);
-        } else {
-            super.setByteArray(tagType, bytes);
-        }
-    }
-
-    private void processCameraSettings(byte[] bytes)
-    {
-        SequentialByteArrayReader reader = new SequentialByteArrayReader(bytes);
-        reader.setMotorolaByteOrder(true);
-
-        int count = bytes.length / 4;
-
-        try {
-            for (int i = 0; i < count; i++) {
-                int value = reader.getInt32();
-                setInt(CameraSettings.OFFSET + i, value);
-            }
-        } catch (IOException e) {
-            // Should never happen, given that we check the length of the bytes beforehand.
-            e.printStackTrace();
-        }
-    }
-
-    public boolean isIntervalMode()
-    {
-        Long value = getLongObject(CameraSettings.TAG_SHOOTING_MODE);
-        return value != null && value == 5;
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    // <summary>
-    // These values are currently decoded only for Olympus models.  Models with
-    // Olympus-style maker notes from other brands such as Acer, BenQ, Hitachi, HP,
-    // Premier, Konica-Minolta, Maginon, Ricoh, Rollei, SeaLife, Sony, Supra,
-    // Vivitar are not listed.
-    // </summary>
-    // <remarks>
-    // Converted from Exiftool version 10.33 created by Phil Harvey
-    // http://www.sno.phy.queensu.ca/~phil/exiftool/
-    // lib\Image\ExifTool\Olympus.pm
-    // </remarks>
-    public static final HashMap<String, String> OlympusCameraTypes = new HashMap<String, String>();
-
-    static {
-        OlympusCameraTypes.put("D4028", "X-2,C-50Z");
-        OlympusCameraTypes.put("D4029", "E-20,E-20N,E-20P");
-        OlympusCameraTypes.put("D4034", "C720UZ");
-        OlympusCameraTypes.put("D4040", "E-1");
-        OlympusCameraTypes.put("D4041", "E-300");
-        OlympusCameraTypes.put("D4083", "C2Z,D520Z,C220Z");
-        OlympusCameraTypes.put("D4106", "u20D,S400D,u400D");
-        OlympusCameraTypes.put("D4120", "X-1");
-        OlympusCameraTypes.put("D4122", "u10D,S300D,u300D");
-        OlympusCameraTypes.put("D4125", "AZ-1");
-        OlympusCameraTypes.put("D4141", "C150,D390");
-        OlympusCameraTypes.put("D4193", "C-5000Z");
-        OlympusCameraTypes.put("D4194", "X-3,C-60Z");
-        OlympusCameraTypes.put("D4199", "u30D,S410D,u410D");
-        OlympusCameraTypes.put("D4205", "X450,D535Z,C370Z");
-        OlympusCameraTypes.put("D4210", "C160,D395");
-        OlympusCameraTypes.put("D4211", "C725UZ");
-        OlympusCameraTypes.put("D4213", "FerrariMODEL2003");
-        OlympusCameraTypes.put("D4216", "u15D");
-        OlympusCameraTypes.put("D4217", "u25D");
-        OlympusCameraTypes.put("D4220", "u-miniD,Stylus V");
-        OlympusCameraTypes.put("D4221", "u40D,S500,uD500");
-        OlympusCameraTypes.put("D4231", "FerrariMODEL2004");
-        OlympusCameraTypes.put("D4240", "X500,D590Z,C470Z");
-        OlympusCameraTypes.put("D4244", "uD800,S800");
-        OlympusCameraTypes.put("D4256", "u720SW,S720SW");
-        OlympusCameraTypes.put("D4261", "X600,D630,FE5500");
-        OlympusCameraTypes.put("D4262", "uD600,S600");
-        OlympusCameraTypes.put("D4301", "u810/S810"); // (yes, "/".  Olympus is not consistent in the notation)
-        OlympusCameraTypes.put("D4302", "u710,S710");
-        OlympusCameraTypes.put("D4303", "u700,S700");
-        OlympusCameraTypes.put("D4304", "FE100,X710");
-        OlympusCameraTypes.put("D4305", "FE110,X705");
-        OlympusCameraTypes.put("D4310", "FE-130,X-720");
-        OlympusCameraTypes.put("D4311", "FE-140,X-725");
-        OlympusCameraTypes.put("D4312", "FE150,X730");
-        OlympusCameraTypes.put("D4313", "FE160,X735");
-        OlympusCameraTypes.put("D4314", "u740,S740");
-        OlympusCameraTypes.put("D4315", "u750,S750");
-        OlympusCameraTypes.put("D4316", "u730/S730");
-        OlympusCameraTypes.put("D4317", "FE115,X715");
-        OlympusCameraTypes.put("D4321", "SP550UZ");
-        OlympusCameraTypes.put("D4322", "SP510UZ");
-        OlympusCameraTypes.put("D4324", "FE170,X760");
-        OlympusCameraTypes.put("D4326", "FE200");
-        OlympusCameraTypes.put("D4327", "FE190/X750"); // (also SX876)
-        OlympusCameraTypes.put("D4328", "u760,S760");
-        OlympusCameraTypes.put("D4330", "FE180/X745"); // (also SX875)
-        OlympusCameraTypes.put("D4331", "u1000/S1000");
-        OlympusCameraTypes.put("D4332", "u770SW,S770SW");
-        OlympusCameraTypes.put("D4333", "FE240/X795");
-        OlympusCameraTypes.put("D4334", "FE210,X775");
-        OlympusCameraTypes.put("D4336", "FE230/X790");
-        OlympusCameraTypes.put("D4337", "FE220,X785");
-        OlympusCameraTypes.put("D4338", "u725SW,S725SW");
-        OlympusCameraTypes.put("D4339", "FE250/X800");
-        OlympusCameraTypes.put("D4341", "u780,S780");
-        OlympusCameraTypes.put("D4343", "u790SW,S790SW");
-        OlympusCameraTypes.put("D4344", "u1020,S1020");
-        OlympusCameraTypes.put("D4346", "FE15,X10");
-        OlympusCameraTypes.put("D4348", "FE280,X820,C520");
-        OlympusCameraTypes.put("D4349", "FE300,X830");
-        OlympusCameraTypes.put("D4350", "u820,S820");
-        OlympusCameraTypes.put("D4351", "u1200,S1200");
-        OlympusCameraTypes.put("D4352", "FE270,X815,C510");
-        OlympusCameraTypes.put("D4353", "u795SW,S795SW");
-        OlympusCameraTypes.put("D4354", "u1030SW,S1030SW");
-        OlympusCameraTypes.put("D4355", "SP560UZ");
-        OlympusCameraTypes.put("D4356", "u1010,S1010");
-        OlympusCameraTypes.put("D4357", "u830,S830");
-        OlympusCameraTypes.put("D4359", "u840,S840");
-        OlympusCameraTypes.put("D4360", "FE350WIDE,X865");
-        OlympusCameraTypes.put("D4361", "u850SW,S850SW");
-        OlympusCameraTypes.put("D4362", "FE340,X855,C560");
-        OlympusCameraTypes.put("D4363", "FE320,X835,C540");
-        OlympusCameraTypes.put("D4364", "SP570UZ");
-        OlympusCameraTypes.put("D4366", "FE330,X845,C550");
-        OlympusCameraTypes.put("D4368", "FE310,X840,C530");
-        OlympusCameraTypes.put("D4370", "u1050SW,S1050SW");
-        OlympusCameraTypes.put("D4371", "u1060,S1060");
-        OlympusCameraTypes.put("D4372", "FE370,X880,C575");
-        OlympusCameraTypes.put("D4374", "SP565UZ");
-        OlympusCameraTypes.put("D4377", "u1040,S1040");
-        OlympusCameraTypes.put("D4378", "FE360,X875,C570");
-        OlympusCameraTypes.put("D4379", "FE20,X15,C25");
-        OlympusCameraTypes.put("D4380", "uT6000,ST6000");
-        OlympusCameraTypes.put("D4381", "uT8000,ST8000");
-        OlympusCameraTypes.put("D4382", "u9000,S9000");
-        OlympusCameraTypes.put("D4384", "SP590UZ");
-        OlympusCameraTypes.put("D4385", "FE3010,X895");
-        OlympusCameraTypes.put("D4386", "FE3000,X890");
-        OlympusCameraTypes.put("D4387", "FE35,X30");
-        OlympusCameraTypes.put("D4388", "u550WP,S550WP");
-        OlympusCameraTypes.put("D4390", "FE5000,X905");
-        OlympusCameraTypes.put("D4391", "u5000");
-        OlympusCameraTypes.put("D4392", "u7000,S7000");
-        OlympusCameraTypes.put("D4396", "FE5010,X915");
-        OlympusCameraTypes.put("D4397", "FE25,X20");
-        OlympusCameraTypes.put("D4398", "FE45,X40");
-        OlympusCameraTypes.put("D4401", "XZ-1");
-        OlympusCameraTypes.put("D4402", "uT6010,ST6010");
-        OlympusCameraTypes.put("D4406", "u7010,S7010 / u7020,S7020");
-        OlympusCameraTypes.put("D4407", "FE4010,X930");
-        OlympusCameraTypes.put("D4408", "X560WP");
-        OlympusCameraTypes.put("D4409", "FE26,X21");
-        OlympusCameraTypes.put("D4410", "FE4000,X920,X925");
-        OlympusCameraTypes.put("D4411", "FE46,X41,X42");
-        OlympusCameraTypes.put("D4412", "FE5020,X935");
-        OlympusCameraTypes.put("D4413", "uTough-3000");
-        OlympusCameraTypes.put("D4414", "StylusTough-6020");
-        OlympusCameraTypes.put("D4415", "StylusTough-8010");
-        OlympusCameraTypes.put("D4417", "u5010,S5010");
-        OlympusCameraTypes.put("D4418", "u7040,S7040");
-        OlympusCameraTypes.put("D4419", "u9010,S9010");
-        OlympusCameraTypes.put("D4423", "FE4040");
-        OlympusCameraTypes.put("D4424", "FE47,X43");
-        OlympusCameraTypes.put("D4426", "FE4030,X950");
-        OlympusCameraTypes.put("D4428", "FE5030,X965,X960");
-        OlympusCameraTypes.put("D4430", "u7030,S7030");
-        OlympusCameraTypes.put("D4432", "SP600UZ");
-        OlympusCameraTypes.put("D4434", "SP800UZ");
-        OlympusCameraTypes.put("D4439", "FE4020,X940");
-        OlympusCameraTypes.put("D4442", "FE5035");
-        OlympusCameraTypes.put("D4448", "FE4050,X970");
-        OlympusCameraTypes.put("D4450", "FE5050,X985");
-        OlympusCameraTypes.put("D4454", "u-7050");
-        OlympusCameraTypes.put("D4464", "T10,X27");
-        OlympusCameraTypes.put("D4470", "FE5040,X980");
-        OlympusCameraTypes.put("D4472", "TG-310");
-        OlympusCameraTypes.put("D4474", "TG-610");
-        OlympusCameraTypes.put("D4476", "TG-810");
-        OlympusCameraTypes.put("D4478", "VG145,VG140,D715");
-        OlympusCameraTypes.put("D4479", "VG130,D710");
-        OlympusCameraTypes.put("D4480", "VG120,D705");
-        OlympusCameraTypes.put("D4482", "VR310,D720");
-        OlympusCameraTypes.put("D4484", "VR320,D725");
-        OlympusCameraTypes.put("D4486", "VR330,D730");
-        OlympusCameraTypes.put("D4488", "VG110,D700");
-        OlympusCameraTypes.put("D4490", "SP-610UZ");
-        OlympusCameraTypes.put("D4492", "SZ-10");
-        OlympusCameraTypes.put("D4494", "SZ-20");
-        OlympusCameraTypes.put("D4496", "SZ-30MR");
-        OlympusCameraTypes.put("D4498", "SP-810UZ");
-        OlympusCameraTypes.put("D4500", "SZ-11");
-        OlympusCameraTypes.put("D4504", "TG-615");
-        OlympusCameraTypes.put("D4508", "TG-620");
-        OlympusCameraTypes.put("D4510", "TG-820");
-        OlympusCameraTypes.put("D4512", "TG-1");
-        OlympusCameraTypes.put("D4516", "SH-21");
-        OlympusCameraTypes.put("D4519", "SZ-14");
-        OlympusCameraTypes.put("D4520", "SZ-31MR");
-        OlympusCameraTypes.put("D4521", "SH-25MR");
-        OlympusCameraTypes.put("D4523", "SP-720UZ");
-        OlympusCameraTypes.put("D4529", "VG170");
-        OlympusCameraTypes.put("D4531", "XZ-2");
-        OlympusCameraTypes.put("D4535", "SP-620UZ");
-        OlympusCameraTypes.put("D4536", "TG-320");
-        OlympusCameraTypes.put("D4537", "VR340,D750");
-        OlympusCameraTypes.put("D4538", "VG160,X990,D745");
-        OlympusCameraTypes.put("D4541", "SZ-12");
-        OlympusCameraTypes.put("D4545", "VH410");
-        OlympusCameraTypes.put("D4546", "XZ-10"); //IB
-        OlympusCameraTypes.put("D4547", "TG-2");
-        OlympusCameraTypes.put("D4548", "TG-830");
-        OlympusCameraTypes.put("D4549", "TG-630");
-        OlympusCameraTypes.put("D4550", "SH-50");
-        OlympusCameraTypes.put("D4553", "SZ-16,DZ-105");
-        OlympusCameraTypes.put("D4562", "SP-820UZ");
-        OlympusCameraTypes.put("D4566", "SZ-15");
-        OlympusCameraTypes.put("D4572", "STYLUS1");
-        OlympusCameraTypes.put("D4574", "TG-3");
-        OlympusCameraTypes.put("D4575", "TG-850");
-        OlympusCameraTypes.put("D4579", "SP-100EE");
-        OlympusCameraTypes.put("D4580", "SH-60");
-        OlympusCameraTypes.put("D4581", "SH-1");
-        OlympusCameraTypes.put("D4582", "TG-835");
-        OlympusCameraTypes.put("D4585", "SH-2 / SH-3");
-        OlympusCameraTypes.put("D4586", "TG-4");
-        OlympusCameraTypes.put("D4587", "TG-860");
-        OlympusCameraTypes.put("D4591", "TG-870");
-        OlympusCameraTypes.put("D4809", "C2500L");
-        OlympusCameraTypes.put("D4842", "E-10");
-        OlympusCameraTypes.put("D4856", "C-1");
-        OlympusCameraTypes.put("D4857", "C-1Z,D-150Z");
-        OlympusCameraTypes.put("DCHC", "D500L");
-        OlympusCameraTypes.put("DCHT", "D600L / D620L");
-        OlympusCameraTypes.put("K0055", "AIR-A01");
-        OlympusCameraTypes.put("S0003", "E-330");
-        OlympusCameraTypes.put("S0004", "E-500");
-        OlympusCameraTypes.put("S0009", "E-400");
-        OlympusCameraTypes.put("S0010", "E-510");
-        OlympusCameraTypes.put("S0011", "E-3");
-        OlympusCameraTypes.put("S0013", "E-410");
-        OlympusCameraTypes.put("S0016", "E-420");
-        OlympusCameraTypes.put("S0017", "E-30");
-        OlympusCameraTypes.put("S0018", "E-520");
-        OlympusCameraTypes.put("S0019", "E-P1");
-        OlympusCameraTypes.put("S0023", "E-620");
-        OlympusCameraTypes.put("S0026", "E-P2");
-        OlympusCameraTypes.put("S0027", "E-PL1");
-        OlympusCameraTypes.put("S0029", "E-450");
-        OlympusCameraTypes.put("S0030", "E-600");
-        OlympusCameraTypes.put("S0032", "E-P3");
-        OlympusCameraTypes.put("S0033", "E-5");
-        OlympusCameraTypes.put("S0034", "E-PL2");
-        OlympusCameraTypes.put("S0036", "E-M5");
-        OlympusCameraTypes.put("S0038", "E-PL3");
-        OlympusCameraTypes.put("S0039", "E-PM1");
-        OlympusCameraTypes.put("S0040", "E-PL1s");
-        OlympusCameraTypes.put("S0042", "E-PL5");
-        OlympusCameraTypes.put("S0043", "E-PM2");
-        OlympusCameraTypes.put("S0044", "E-P5");
-        OlympusCameraTypes.put("S0045", "E-PL6");
-        OlympusCameraTypes.put("S0046", "E-PL7"); //IB
-        OlympusCameraTypes.put("S0047", "E-M1");
-        OlympusCameraTypes.put("S0051", "E-M10");
-        OlympusCameraTypes.put("S0052", "E-M5MarkII"); //IB
-        OlympusCameraTypes.put("S0059", "E-M10MarkII");
-        OlympusCameraTypes.put("S0061", "PEN-F"); //forum7005
-        OlympusCameraTypes.put("S0065", "E-PL8");
-        OlympusCameraTypes.put("S0067", "E-M1MarkII");
-        OlympusCameraTypes.put("SR45", "D220");
-        OlympusCameraTypes.put("SR55", "D320L");
-        OlympusCameraTypes.put("SR83", "D340L");
-        OlympusCameraTypes.put("SR85", "C830L,D340R");
-        OlympusCameraTypes.put("SR852", "C860L,D360L");
-        OlympusCameraTypes.put("SR872", "C900Z,D400Z");
-        OlympusCameraTypes.put("SR874", "C960Z,D460Z");
-        OlympusCameraTypes.put("SR951", "C2000Z");
-        OlympusCameraTypes.put("SR952", "C21");
-        OlympusCameraTypes.put("SR953", "C21T.commu");
-        OlympusCameraTypes.put("SR954", "C2020Z");
-        OlympusCameraTypes.put("SR955", "C990Z,D490Z");
-        OlympusCameraTypes.put("SR956", "C211Z");
-        OlympusCameraTypes.put("SR959", "C990ZS,D490Z");
-        OlympusCameraTypes.put("SR95A", "C2100UZ");
-        OlympusCameraTypes.put("SR971", "C100,D370");
-        OlympusCameraTypes.put("SR973", "C2,D230");
-        OlympusCameraTypes.put("SX151", "E100RS");
-        OlympusCameraTypes.put("SX351", "C3000Z / C3030Z");
-        OlympusCameraTypes.put("SX354", "C3040Z");
-        OlympusCameraTypes.put("SX355", "C2040Z");
-        OlympusCameraTypes.put("SX357", "C700UZ");
-        OlympusCameraTypes.put("SX358", "C200Z,D510Z");
-        OlympusCameraTypes.put("SX374", "C3100Z,C3020Z");
-        OlympusCameraTypes.put("SX552", "C4040Z");
-        OlympusCameraTypes.put("SX553", "C40Z,D40Z");
-        OlympusCameraTypes.put("SX556", "C730UZ");
-        OlympusCameraTypes.put("SX558", "C5050Z");
-        OlympusCameraTypes.put("SX571", "C120,D380");
-        OlympusCameraTypes.put("SX574", "C300Z,D550Z");
-        OlympusCameraTypes.put("SX575", "C4100Z,C4000Z");
-        OlympusCameraTypes.put("SX751", "X200,D560Z,C350Z");
-        OlympusCameraTypes.put("SX752", "X300,D565Z,C450Z");
-        OlympusCameraTypes.put("SX753", "C750UZ");
-        OlympusCameraTypes.put("SX754", "C740UZ");
-        OlympusCameraTypes.put("SX755", "C755UZ");
-        OlympusCameraTypes.put("SX756", "C5060WZ");
-        OlympusCameraTypes.put("SX757", "C8080WZ");
-        OlympusCameraTypes.put("SX758", "X350,D575Z,C360Z");
-        OlympusCameraTypes.put("SX759", "X400,D580Z,C460Z");
-        OlympusCameraTypes.put("SX75A", "AZ-2ZOOM");
-        OlympusCameraTypes.put("SX75B", "D595Z,C500Z");
-        OlympusCameraTypes.put("SX75C", "X550,D545Z,C480Z");
-        OlympusCameraTypes.put("SX75D", "IR-300");
-        OlympusCameraTypes.put("SX75F", "C55Z,C5500Z");
-        OlympusCameraTypes.put("SX75G", "C170,D425");
-        OlympusCameraTypes.put("SX75J", "C180,D435");
-        OlympusCameraTypes.put("SX771", "C760UZ");
-        OlympusCameraTypes.put("SX772", "C770UZ");
-        OlympusCameraTypes.put("SX773", "C745UZ");
-        OlympusCameraTypes.put("SX774", "X250,D560Z,C350Z");
-        OlympusCameraTypes.put("SX775", "X100,D540Z,C310Z");
-        OlympusCameraTypes.put("SX776", "C460ZdelSol");
-        OlympusCameraTypes.put("SX777", "C765UZ");
-        OlympusCameraTypes.put("SX77A", "D555Z,C315Z");
-        OlympusCameraTypes.put("SX851", "C7070WZ");
-        OlympusCameraTypes.put("SX852", "C70Z,C7000Z");
-        OlympusCameraTypes.put("SX853", "SP500UZ");
-        OlympusCameraTypes.put("SX854", "SP310");
-        OlympusCameraTypes.put("SX855", "SP350");
-        OlympusCameraTypes.put("SX873", "SP320");
-        OlympusCameraTypes.put("SX875", "FE180/X745"); // (also D4330)
-        OlympusCameraTypes.put("SX876", "FE190/X750"); // (also D4327)
-
-        //   other brands
-        //    4MP9Q3", "Camera 4MP-9Q3'
-        //    4MP9T2", "BenQ DC C420 / Camera 4MP-9T2'
-        //    5MP9Q3", "Camera 5MP-9Q3" },
-        //    5MP9X9", "Camera 5MP-9X9" },
-        //   '5MP-9T'=> 'Camera 5MP-9T3" },
-        //   '5MP-9Y'=> 'Camera 5MP-9Y2" },
-        //   '6MP-9U'=> 'Camera 6MP-9U9" },
-        //    7MP9Q3", "Camera 7MP-9Q3" },
-        //   '8MP-9U'=> 'Camera 8MP-9U4" },
-        //    CE5330", "Acer CE-5330" },
-        //   'CP-853'=> 'Acer CP-8531" },
-        //    CS5531", "Acer CS5531" },
-        //    DC500 ", "SeaLife DC500" },
-        //    DC7370", "Camera 7MP-9GA" },
-        //    DC7371", "Camera 7MP-9GM" },
-        //    DC7371", "Hitachi HDC-751E" },
-        //    DC7375", "Hitachi HDC-763E / Rollei RCP-7330X / Ricoh Caplio RR770 / Vivitar ViviCam 7330" },
-        //   'DC E63'=> 'BenQ DC E63+" },
-        //   'DC P86'=> 'BenQ DC P860" },
-        //    DS5340", "Maginon Performic S5 / Premier 5MP-9M7" },
-        //    DS5341", "BenQ E53+ / Supra TCM X50 / Maginon X50 / Premier 5MP-9P8" },
-        //    DS5346", "Premier 5MP-9Q2" },
-        //    E500  ", "Konica Minolta DiMAGE E500" },
-        //    MAGINO", "Maginon X60" },
-        //    Mz60  ", "HP Photosmart Mz60" },
-        //    Q3DIGI", "Camera 5MP-9Q3" },
-        //    SLIMLI", "Supra Slimline X6" },
-        //    V8300s", "Vivitar V8300s" },
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopment2MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopment2MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,234 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.util.HashMap;
-
-import static com.drew.metadata.exif.makernotes.OlympusRawDevelopment2MakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusRawDevelopment2MakernoteDirectory}.
- * <p>
- * Some Description functions converted from Exiftool version 10.10 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusRawDevelopment2MakernoteDescriptor extends TagDescriptor<OlympusRawDevelopment2MakernoteDirectory>
-{
-    public OlympusRawDevelopment2MakernoteDescriptor(@NotNull OlympusRawDevelopment2MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagRawDevVersion:
-                return getRawDevVersionDescription();
-            case TagRawDevExposureBiasValue:
-                return getRawDevExposureBiasValueDescription();
-            case TagRawDevColorSpace:
-                return getRawDevColorSpaceDescription();
-            case TagRawDevNoiseReduction:
-                return getRawDevNoiseReductionDescription();
-            case TagRawDevEngine:
-                return getRawDevEngineDescription();
-            case TagRawDevPictureMode:
-                return getRawDevPictureModeDescription();
-            case TagRawDevPmBwFilter:
-                return getRawDevPmBwFilterDescription();
-            case TagRawDevPmPictureTone:
-                return getRawDevPmPictureToneDescription();
-            case TagRawDevArtFilter:
-                return getRawDevArtFilterDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getRawDevVersionDescription()
-    {
-        return getVersionBytesDescription(TagRawDevVersion, 4);
-    }
-
-    @Nullable
-    public String getRawDevExposureBiasValueDescription()
-    {
-        return getIndexedDescription(TagRawDevExposureBiasValue,
-                1, "Color Temperature", "Gray Point");
-    }
-
-    @Nullable
-    public String getRawDevColorSpaceDescription()
-    {
-        return getIndexedDescription(TagRawDevColorSpace,
-            "sRGB", "Adobe RGB", "Pro Photo RGB");
-    }
-
-    @Nullable
-    public String getRawDevNoiseReductionDescription()
-    {
-        Integer value = _directory.getInteger(TagRawDevNoiseReduction);
-        if (value == null)
-            return null;
-
-        if (value == 0)
-            return "(none)";
-
-        StringBuilder sb = new StringBuilder();
-        int v = value;
-
-        if ((v        & 1) != 0) sb.append("Noise Reduction, ");
-        if (((v >> 1) & 1) != 0) sb.append("Noise Filter, ");
-        if (((v >> 2) & 1) != 0) sb.append("Noise Filter (ISO Boost), ");
-        if (((v >> 3) & 1) != 0) sb.append("Noise Filter (Auto), ");
-        
-        if (sb.length() > 2) {
-            sb.delete(sb.length() - 2, sb.length());
-        }
-        return sb.toString();
-    }
-
-    @Nullable
-    public String getRawDevEngineDescription()
-    {
-        return getIndexedDescription(TagRawDevEngine,
-            "High Speed", "High Function", "Advanced High Speed", "Advanced High Function");
-    }
-
-    @Nullable
-    public String getRawDevPictureModeDescription()
-    {
-        Integer value = _directory.getInteger(TagRawDevPictureMode);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 1:
-                return "Vivid";
-            case 2:
-                return "Natural";
-            case 3:
-                return "Muted";
-            case 256:
-                return "Monotone";
-            case 512:
-                return "Sepia";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getRawDevPmBwFilterDescription()
-    {
-        return getIndexedDescription(TagRawDevPmBwFilter,
-            "Neutral", "Yellow", "Orange", "Red", "Green");
-    }
-
-    @Nullable
-    public String getRawDevPmPictureToneDescription()
-    {
-        return getIndexedDescription(TagRawDevPmPictureTone,
-            "Neutral", "Sepia", "Blue", "Purple", "Green");
-    }
-
-    @Nullable
-    public String getRawDevArtFilterDescription()
-    {
-        return getFilterDescription(TagRawDevArtFilter);
-    }
-
-    @Nullable
-    public String getFilterDescription(int tag)
-    {
-        int[] values = _directory.getIntArray(tag);
-        if (values == null || values.length == 0)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if (i == 0)
-                sb.append(_filters.containsKey(values[i]) ? _filters.get(values[i]) : "[unknown]");
-            else
-                sb.append(values[i]).append("; ");
-            sb.append("; ");
-        }
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    // RawDevArtFilter values
-    private static final HashMap<Integer, String> _filters = new HashMap<Integer, String>();
-
-    static {
-        _filters.put(0, "Off");
-        _filters.put(1, "Soft Focus");
-        _filters.put(2, "Pop Art");
-        _filters.put(3, "Pale & Light Color");
-        _filters.put(4, "Light Tone");
-        _filters.put(5, "Pin Hole");
-        _filters.put(6, "Grainy Film");
-        _filters.put(9, "Diorama");
-        _filters.put(10, "Cross Process");
-        _filters.put(12, "Fish Eye");
-        _filters.put(13, "Drawing");
-        _filters.put(14, "Gentle Sepia");
-        _filters.put(15, "Pale & Light Color II");
-        _filters.put(16, "Pop Art II");
-        _filters.put(17, "Pin Hole II");
-        _filters.put(18, "Pin Hole III");
-        _filters.put(19, "Grainy Film II");
-        _filters.put(20, "Dramatic Tone");
-        _filters.put(21, "Punk");
-        _filters.put(22, "Soft Focus 2");
-        _filters.put(23, "Sparkle");
-        _filters.put(24, "Watercolor");
-        _filters.put(25, "Key Line");
-        _filters.put(26, "Key Line II");
-        _filters.put(27, "Miniature");
-        _filters.put(28, "Reflection");
-        _filters.put(29, "Fragmented");
-        _filters.put(31, "Cross Process II");
-        _filters.put(32, "Dramatic Tone II");
-        _filters.put(33, "Watercolor I");
-        _filters.put(34, "Watercolor II");
-        _filters.put(35, "Diorama II");
-        _filters.put(36, "Vintage");
-        _filters.put(37, "Vintage II");
-        _filters.put(38, "Vintage III");
-        _filters.put(39, "Partial Color");
-        _filters.put(40, "Partial Color II");
-        _filters.put(41, "Partial Color III");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopment2MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopment2MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,111 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * The Olympus raw development 2 makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusRawDevelopment2MakernoteDirectory extends Directory
-{    
-    public static final int TagRawDevVersion = 0x0000;
-    public static final int TagRawDevExposureBiasValue = 0x0100;
-    public static final int TagRawDevWhiteBalance = 0x0101;
-    public static final int TagRawDevWhiteBalanceValue = 0x0102;
-    public static final int TagRawDevWbFineAdjustment = 0x0103;
-    public static final int TagRawDevGrayPoint = 0x0104;
-    public static final int TagRawDevContrastValue = 0x0105;
-    public static final int TagRawDevSharpnessValue = 0x0106;
-    public static final int TagRawDevSaturationEmphasis = 0x0107;
-    public static final int TagRawDevMemoryColorEmphasis = 0x0108;
-    public static final int TagRawDevColorSpace = 0x0109;
-    public static final int TagRawDevNoiseReduction = 0x010a;
-    public static final int TagRawDevEngine = 0x010b;
-    public static final int TagRawDevPictureMode = 0x010c;
-    public static final int TagRawDevPmSaturation = 0x010d;
-    public static final int TagRawDevPmContrast = 0x010e;
-    public static final int TagRawDevPmSharpness = 0x010f;
-    public static final int TagRawDevPmBwFilter = 0x0110;
-    public static final int TagRawDevPmPictureTone = 0x0111;
-    public static final int TagRawDevGradation = 0x0112;
-    public static final int TagRawDevSaturation3 = 0x0113;
-    public static final int TagRawDevAutoGradation = 0x0119;
-    public static final int TagRawDevPmNoiseFilter = 0x0120;
-    public static final int TagRawDevArtFilter = 0x0121;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {        
-        _tagNameMap.put(TagRawDevVersion, "Raw Dev Version");
-        _tagNameMap.put(TagRawDevExposureBiasValue, "Raw Dev Exposure Bias Value");
-        _tagNameMap.put(TagRawDevWhiteBalance, "Raw Dev White Balance");
-        _tagNameMap.put(TagRawDevWhiteBalanceValue, "Raw Dev White Balance Value");
-        _tagNameMap.put(TagRawDevWbFineAdjustment, "Raw Dev WB Fine Adjustment");
-        _tagNameMap.put(TagRawDevGrayPoint, "Raw Dev Gray Point");
-        _tagNameMap.put(TagRawDevContrastValue, "Raw Dev Contrast Value");
-        _tagNameMap.put(TagRawDevSharpnessValue, "Raw Dev Sharpness Value");
-        _tagNameMap.put(TagRawDevSaturationEmphasis, "Raw Dev Saturation Emphasis");
-        _tagNameMap.put(TagRawDevMemoryColorEmphasis, "Raw Dev Memory Color Emphasis");
-        _tagNameMap.put(TagRawDevColorSpace, "Raw Dev Color Space");
-        _tagNameMap.put(TagRawDevNoiseReduction, "Raw Dev Noise Reduction");
-        _tagNameMap.put(TagRawDevEngine, "Raw Dev Engine");
-        _tagNameMap.put(TagRawDevPictureMode, "Raw Dev Picture Mode");
-        _tagNameMap.put(TagRawDevPmSaturation, "Raw Dev PM Saturation");
-        _tagNameMap.put(TagRawDevPmContrast, "Raw Dev PM Contrast");
-        _tagNameMap.put(TagRawDevPmSharpness, "Raw Dev PM Sharpness");
-        _tagNameMap.put(TagRawDevPmBwFilter, "Raw Dev PM BW Filter");
-        _tagNameMap.put(TagRawDevPmPictureTone, "Raw Dev PM Picture Tone");
-        _tagNameMap.put(TagRawDevGradation, "Raw Dev Gradation");
-        _tagNameMap.put(TagRawDevSaturation3, "Raw Dev Saturation 3");
-        _tagNameMap.put(TagRawDevAutoGradation, "Raw Dev Auto Gradation");
-        _tagNameMap.put(TagRawDevPmNoiseFilter, "Raw Dev PM Noise Filter");
-        _tagNameMap.put(TagRawDevArtFilter, "Raw Dev Art Filter");
-    }
-
-    public OlympusRawDevelopment2MakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusRawDevelopment2MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Raw Development 2";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopmentMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopmentMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,155 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.OlympusRawDevelopmentMakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusRawDevelopmentMakernoteDirectory}.
- * <p>
- * Some Description functions converted from Exiftool version 10.10 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusRawDevelopmentMakernoteDescriptor extends TagDescriptor<OlympusRawDevelopmentMakernoteDirectory>
-{
-    public OlympusRawDevelopmentMakernoteDescriptor(@NotNull OlympusRawDevelopmentMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagRawDevVersion:
-                return getRawDevVersionDescription();
-            case TagRawDevColorSpace:
-                return getRawDevColorSpaceDescription();
-            case TagRawDevEngine:
-                return getRawDevEngineDescription();
-            case TagRawDevNoiseReduction:
-                return getRawDevNoiseReductionDescription();
-            case TagRawDevEditStatus:
-                return getRawDevEditStatusDescription();
-            case TagRawDevSettings:
-                return getRawDevSettingsDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getRawDevVersionDescription()
-    {
-        return getVersionBytesDescription(TagRawDevVersion, 4);
-    }
-
-    @Nullable
-    public String getRawDevColorSpaceDescription()
-    {
-        return getIndexedDescription(TagRawDevColorSpace,
-            "sRGB", "Adobe RGB", "Pro Photo RGB");
-    }
-
-    @Nullable
-    public String getRawDevEngineDescription()
-    {
-        return getIndexedDescription(TagRawDevEngine,
-            "High Speed", "High Function", "Advanced High Speed", "Advanced High Function");
-    }
-
-    @Nullable
-    public String getRawDevNoiseReductionDescription()
-    {
-        Integer value = _directory.getInteger(TagRawDevNoiseReduction);
-        if (value == null)
-            return null;
-
-        if (value == 0)
-            return "(none)";
-
-        StringBuilder sb = new StringBuilder();
-        int v = value;
-
-        if ((v        & 1) != 0) sb.append("Noise Reduction, ");
-        if (((v >> 1) & 1) != 0) sb.append("Noise Filter, ");
-        if (((v >> 2) & 1) != 0) sb.append("Noise Filter (ISO Boost), ");
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-    @Nullable
-    public String getRawDevEditStatusDescription()
-    {
-        Integer value = _directory.getInteger(TagRawDevEditStatus);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 0:
-                return "Original";
-            case 1:
-                return "Edited (Landscape)";
-            case 6:
-            case 8:
-                return "Edited (Portrait)";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getRawDevSettingsDescription()
-    {
-        Integer value = _directory.getInteger(TagRawDevSettings);
-        if (value == null)
-            return null;
-
-        if (value == 0)
-            return "(none)";
-
-        StringBuilder sb = new StringBuilder();
-        int v = value;
-
-        if ((v        & 1) != 0) sb.append("WB Color Temp, ");
-        if (((v >> 1) & 1) != 0) sb.append("WB Gray Point, ");
-        if (((v >> 2) & 1) != 0) sb.append("Saturation, ");
-        if (((v >> 3) & 1) != 0) sb.append("Contrast, ");
-        if (((v >> 4) & 1) != 0) sb.append("Sharpness, ");
-        if (((v >> 5) & 1) != 0) sb.append("Color Space, ");
-        if (((v >> 6) & 1) != 0) sb.append("High Function, ");
-        if (((v >> 7) & 1) != 0) sb.append("Noise Reduction, ");
-
-        return sb.substring(0, sb.length() - 2);
-    }
-
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopmentMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusRawDevelopmentMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,91 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * The Olympus raw development makernote is used by many manufacturers (Epson, Konica, Minolta and Agfa...), and as such contains some tags
- * that appear specific to those manufacturers.
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusRawDevelopmentMakernoteDirectory extends Directory
-{
-    public static final int TagRawDevVersion = 0x0000;
-    public static final int TagRawDevExposureBiasValue = 0x0100;
-    public static final int TagRawDevWhiteBalanceValue = 0x0101;
-    public static final int TagRawDevWbFineAdjustment = 0x0102;
-    public static final int TagRawDevGrayPoint = 0x0103;
-    public static final int TagRawDevSaturationEmphasis = 0x0104;
-    public static final int TagRawDevMemoryColorEmphasis = 0x0105;
-    public static final int TagRawDevContrastValue = 0x0106;
-    public static final int TagRawDevSharpnessValue = 0x0107;
-    public static final int TagRawDevColorSpace = 0x0108;
-    public static final int TagRawDevEngine = 0x0109;
-    public static final int TagRawDevNoiseReduction = 0x010a;
-    public static final int TagRawDevEditStatus = 0x010b;
-    public static final int TagRawDevSettings = 0x010c;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TagRawDevVersion, "Raw Dev Version");
-        _tagNameMap.put(TagRawDevExposureBiasValue, "Raw Dev Exposure Bias Value");
-        _tagNameMap.put(TagRawDevWhiteBalanceValue, "Raw Dev White Balance Value");
-        _tagNameMap.put(TagRawDevWbFineAdjustment, "Raw Dev WB Fine Adjustment");
-        _tagNameMap.put(TagRawDevGrayPoint, "Raw Dev Gray Point");
-        _tagNameMap.put(TagRawDevSaturationEmphasis, "Raw Dev Saturation Emphasis");
-        _tagNameMap.put(TagRawDevMemoryColorEmphasis, "Raw Dev Memory Color Emphasis");
-        _tagNameMap.put(TagRawDevContrastValue, "Raw Dev Contrast Value");
-        _tagNameMap.put(TagRawDevSharpnessValue, "Raw Dev Sharpness Value");
-        _tagNameMap.put(TagRawDevColorSpace, "Raw Dev Color Space");
-        _tagNameMap.put(TagRawDevEngine, "Raw Dev Engine");
-        _tagNameMap.put(TagRawDevNoiseReduction, "Raw Dev Noise Reduction");
-        _tagNameMap.put(TagRawDevEditStatus, "Raw Dev Edit Status");
-        _tagNameMap.put(TagRawDevSettings, "Raw Dev Settings");
-    }
-
-    public OlympusRawDevelopmentMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusRawDevelopmentMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Raw Development";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusRawInfoMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusRawInfoMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,141 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.OlympusRawInfoMakernoteDirectory.*;
-
-/**
- * Provides human-readable String representations of tag values stored in a {@link OlympusRawInfoMakernoteDirectory}.
- * <p>
- * Some Description functions converted from Exiftool version 10.33 created by Phil Harvey
- * http://www.sno.phy.queensu.ca/~phil/exiftool/
- * lib\Image\ExifTool\Olympus.pm
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusRawInfoMakernoteDescriptor extends TagDescriptor<OlympusRawInfoMakernoteDirectory>
-{
-    public OlympusRawInfoMakernoteDescriptor(@NotNull OlympusRawInfoMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagRawInfoVersion:
-                return getVersionBytesDescription(TagRawInfoVersion, 4);
-            case TagColorMatrix2:
-                return getColorMatrix2Description();
-            case TagYCbCrCoefficients:
-                return getYCbCrCoefficientsDescription();
-            case TagLightSource:
-                return getOlympusLightSourceDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getColorMatrix2Description()
-    {
-        int[] values = _directory.getIntArray(TagColorMatrix2);
-        if (values == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            sb.append((short)values[i]);
-            if (i < values.length - 1)
-                sb.append(" ");
-        }
-        return sb.length() == 0 ? null : sb.toString();
-    }
-
-    @Nullable
-    public String getYCbCrCoefficientsDescription()
-    {
-        int[] values = _directory.getIntArray(TagYCbCrCoefficients);
-        if (values == null)
-            return null;
-
-        Rational[] ret = new Rational[values.length / 2];
-        for(int i = 0; i < values.length / 2; i++)
-        {
-            ret[i] = new Rational((short)values[2*i], (short)values[2*i + 1]);
-        }
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < ret.length; i++) {
-            sb.append(ret[i].doubleValue());
-            if (i < ret.length - 1)
-                sb.append(" ");
-        }
-        return sb.length() == 0 ? null : sb.toString();
-    }
-    
-    @Nullable
-    public String getOlympusLightSourceDescription()
-    {
-        Integer value = _directory.getInteger(TagLightSource);
-        if (value == null)
-            return null;
-
-        switch (value.shortValue())
-        {
-            case 0:
-                return "Unknown";
-            case 16:
-                return "Shade";
-            case 17:
-                return "Cloudy";
-            case 18:
-                return "Fine Weather";
-            case 20:
-                return "Tungsten (Incandescent)";
-            case 22:
-                return "Evening Sunlight";
-            case 33:
-                return "Daylight Fluorescent";
-            case 34:
-                return "Day White Fluorescent";
-            case 35:
-                return "Cool White Fluorescent";
-            case 36:
-                return "White Fluorescent";
-            case 256:
-                return "One Touch White Balance";
-            case 512:
-                return "Custom 1-4";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/OlympusRawInfoMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/OlympusRawInfoMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,142 +1,0 @@
-/*
- * Copyright 2002-2015 Drew Noakes
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * These tags are found only in ORF images of some models (eg. C8080WZ)
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class OlympusRawInfoMakernoteDirectory extends Directory
-{
-    public static final int TagRawInfoVersion = 0x0000;
-    public static final int TagWbRbLevelsUsed = 0x0100;
-    public static final int TagWbRbLevelsAuto = 0x0110;
-    public static final int TagWbRbLevelsShade = 0x0120;
-    public static final int TagWbRbLevelsCloudy = 0x0121;
-    public static final int TagWbRbLevelsFineWeather = 0x0122;
-    public static final int TagWbRbLevelsTungsten = 0x0123;
-    public static final int TagWbRbLevelsEveningSunlight = 0x0124;
-    public static final int TagWbRbLevelsDaylightFluor = 0x0130;
-    public static final int TagWbRbLevelsDayWhiteFluor = 0x0131;
-    public static final int TagWbRbLevelsCoolWhiteFluor = 0x0132;
-    public static final int TagWbRbLevelsWhiteFluorescent = 0x0133;
-
-    public static final int TagColorMatrix2 = 0x0200;
-    public static final int TagCoringFilter = 0x0310;
-    public static final int TagCoringValues = 0x0311;
-    public static final int TagBlackLevel2 = 0x0600;
-    public static final int TagYCbCrCoefficients = 0x0601;
-    public static final int TagValidPixelDepth = 0x0611;
-    public static final int TagCropLeft = 0x0612;
-    public static final int TagCropTop = 0x0613;
-    public static final int TagCropWidth = 0x0614;
-    public static final int TagCropHeight = 0x0615;
-
-    public static final int TagLightSource = 0x1000;
-
-    //the following 5 tags all have 3 values: val, min, max
-    public static final int TagWhiteBalanceComp = 0x1001;
-    public static final int TagSaturationSetting = 0x1010;
-    public static final int TagHueSetting = 0x1011;
-    public static final int TagContrastSetting = 0x1012;
-    public static final int TagSharpnessSetting = 0x1013;
-
-    // settings written by Camedia Master 4.x
-    public static final int TagCmExposureCompensation = 0x2000;
-    public static final int TagCmWhiteBalance = 0x2001;
-    public static final int TagCmWhiteBalanceComp = 0x2002;
-    public static final int TagCmWhiteBalanceGrayPoint = 0x2010;
-    public static final int TagCmSaturation = 0x2020;
-    public static final int TagCmHue = 0x2021;
-    public static final int TagCmContrast = 0x2022;
-    public static final int TagCmSharpness = 0x2023;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TagRawInfoVersion, "Raw Info Version");
-        _tagNameMap.put(TagWbRbLevelsUsed, "WB RB Levels Used");
-        _tagNameMap.put(TagWbRbLevelsAuto, "WB RB Levels Auto");
-        _tagNameMap.put(TagWbRbLevelsShade, "WB RB Levels Shade");
-        _tagNameMap.put(TagWbRbLevelsCloudy, "WB RB Levels Cloudy");
-        _tagNameMap.put(TagWbRbLevelsFineWeather, "WB RB Levels Fine Weather");
-        _tagNameMap.put(TagWbRbLevelsTungsten, "WB RB Levels Tungsten");
-        _tagNameMap.put(TagWbRbLevelsEveningSunlight, "WB RB Levels Evening Sunlight");
-        _tagNameMap.put(TagWbRbLevelsDaylightFluor, "WB RB Levels Daylight Fluor");
-        _tagNameMap.put(TagWbRbLevelsDayWhiteFluor, "WB RB Levels Day White Fluor");
-        _tagNameMap.put(TagWbRbLevelsCoolWhiteFluor, "WB RB Levels Cool White Fluor");
-        _tagNameMap.put(TagWbRbLevelsWhiteFluorescent, "WB RB Levels White Fluorescent");
-        _tagNameMap.put(TagColorMatrix2, "Color Matrix 2");
-        _tagNameMap.put(TagCoringFilter, "Coring Filter");
-        _tagNameMap.put(TagCoringValues, "Coring Values");
-        _tagNameMap.put(TagBlackLevel2, "Black Level 2");
-        _tagNameMap.put(TagYCbCrCoefficients, "YCbCrCoefficients");
-        _tagNameMap.put(TagValidPixelDepth, "Valid Pixel Depth");
-        _tagNameMap.put(TagCropLeft, "Crop Left");
-        _tagNameMap.put(TagCropTop, "Crop Top");
-        _tagNameMap.put(TagCropWidth, "Crop Width");
-        _tagNameMap.put(TagCropHeight, "Crop Height");
-        _tagNameMap.put(TagLightSource, "Light Source");
-
-        _tagNameMap.put(TagWhiteBalanceComp, "White Balance Comp");
-        _tagNameMap.put(TagSaturationSetting, "Saturation Setting");
-        _tagNameMap.put(TagHueSetting, "Hue Setting");
-        _tagNameMap.put(TagContrastSetting, "Contrast Setting");
-        _tagNameMap.put(TagSharpnessSetting, "Sharpness Setting");
-
-        _tagNameMap.put(TagCmExposureCompensation, "CM Exposure Compensation");
-        _tagNameMap.put(TagCmWhiteBalance, "CM White Balance");
-        _tagNameMap.put(TagCmWhiteBalanceComp, "CM White Balance Comp");
-        _tagNameMap.put(TagCmWhiteBalanceGrayPoint, "CM White Balance Gray Point");
-        _tagNameMap.put(TagCmSaturation, "CM Saturation");
-        _tagNameMap.put(TagCmHue, "CM Hue");
-        _tagNameMap.put(TagCmContrast, "CM Contrast");
-        _tagNameMap.put(TagCmSharpness, "CM Sharpness");
-    }
-
-    public OlympusRawInfoMakernoteDirectory()
-    {
-        this.setDescriptor(new OlympusRawInfoMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Olympus Raw Info";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,921 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.Charsets;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Age;
-import com.drew.metadata.Face;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DecimalFormat;
-import java.io.IOException;
-
-import static com.drew.metadata.exif.makernotes.PanasonicMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PanasonicMakernoteDirectory}.
- * <p>
- * Some information about this makernote taken from here:
- * <ul>
- * <li><a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html">http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html</a></li>
- * <li><a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html">http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html</a></li>
- * </ul>
- *
- * @author Drew Noakes https://drewnoakes.com
- * @author Philipp Sandhaus
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicMakernoteDescriptor extends TagDescriptor<PanasonicMakernoteDirectory>
-{
-    public PanasonicMakernoteDescriptor(@NotNull PanasonicMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_QUALITY_MODE:
-                return getQualityModeDescription();
-            case TAG_FIRMWARE_VERSION:
-                return getVersionDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_FOCUS_MODE:
-                return getFocusModeDescription();
-            case TAG_AF_AREA_MODE:
-                return getAfAreaModeDescription();
-            case TAG_IMAGE_STABILIZATION:
-                return getImageStabilizationDescription();
-            case TAG_MACRO_MODE:
-                return getMacroModeDescription();
-            case TAG_RECORD_MODE:
-                return getRecordModeDescription();
-            case TAG_AUDIO:
-                return getAudioDescription();
-            case TAG_UNKNOWN_DATA_DUMP:
-                return getUnknownDataDumpDescription();
-            case TAG_COLOR_EFFECT:
-                return getColorEffectDescription();
-            case TAG_UPTIME:
-                return getUptimeDescription();
-            case TAG_BURST_MODE:
-                return getBurstModeDescription();
-            case TAG_CONTRAST_MODE:
-                return getContrastModeDescription();
-            case TAG_NOISE_REDUCTION:
-                return getNoiseReductionDescription();
-            case TAG_SELF_TIMER:
-                return getSelfTimerDescription();
-            case TAG_ROTATION:
-                return getRotationDescription();
-            case TAG_AF_ASSIST_LAMP:
-                return getAfAssistLampDescription();
-            case TAG_COLOR_MODE:
-                return getColorModeDescription();
-            case TAG_OPTICAL_ZOOM_MODE:
-                return getOpticalZoomModeDescription();
-            case TAG_CONVERSION_LENS:
-                return getConversionLensDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_WORLD_TIME_LOCATION:
-                return getWorldTimeLocationDescription();
-            case TAG_ADVANCED_SCENE_MODE:
-                return getAdvancedSceneModeDescription();
-            case TAG_FACE_DETECTION_INFO:
-                return getDetectedFacesDescription();
-            case TAG_TRANSFORM:
-                return getTransformDescription();
-            case TAG_TRANSFORM_1:
-                return getTransform1Description();
-            case TAG_INTELLIGENT_EXPOSURE:
-                return getIntelligentExposureDescription();
-            case TAG_FLASH_WARNING:
-                return getFlashWarningDescription();
-            case TAG_COUNTRY:
-                return getCountryDescription();
-            case TAG_STATE:
-                return getStateDescription();
-            case TAG_CITY:
-                return getCityDescription();
-            case TAG_LANDMARK:
-                return getLandmarkDescription();
-            case TAG_INTELLIGENT_RESOLUTION:
-                return getIntelligentResolutionDescription();
-            case TAG_FACE_RECOGNITION_INFO:
-                return getRecognizedFacesDescription();
-            case TAG_SCENE_MODE:
-                return getSceneModeDescription();
-            case TAG_FLASH_FIRED:
-                return getFlashFiredDescription();
-            case TAG_TEXT_STAMP:
-                return getTextStampDescription();
-            case TAG_TEXT_STAMP_1:
-                return getTextStamp1Description();
-            case TAG_TEXT_STAMP_2:
-                return getTextStamp2Description();
-            case TAG_TEXT_STAMP_3:
-                return getTextStamp3Description();
-            case TAG_MAKERNOTE_VERSION:
-                return getMakernoteVersionDescription();
-            case TAG_EXIF_VERSION:
-                return getExifVersionDescription();
-            case TAG_INTERNAL_SERIAL_NUMBER:
-                return getInternalSerialNumberDescription();
-            case TAG_TITLE:
-                return getTitleDescription();
-            case TAG_BRACKET_SETTINGS:
-                return getBracketSettingsDescription();
-            case TAG_FLASH_CURTAIN:
-                return getFlashCurtainDescription();
-            case TAG_LONG_EXPOSURE_NOISE_REDUCTION:
-                return getLongExposureNoiseReductionDescription();
-            case TAG_BABY_NAME:
-                return getBabyNameDescription();
-            case TAG_LOCATION:
-                return getLocationDescription();
-
-            case TAG_LENS_FIRMWARE_VERSION:
-                return getLensFirmwareVersionDescription();
-            case TAG_INTELLIGENT_D_RANGE:
-                return getIntelligentDRangeDescription();
-            case TAG_CLEAR_RETOUCH:
-                return getClearRetouchDescription();
-            case TAG_PHOTO_STYLE:
-                return getPhotoStyleDescription();
-            case TAG_SHADING_COMPENSATION:
-                return getShadingCompensationDescription();
-
-            case TAG_ACCELEROMETER_Z:
-                return getAccelerometerZDescription();
-            case TAG_ACCELEROMETER_X:
-                return getAccelerometerXDescription();
-            case TAG_ACCELEROMETER_Y:
-                return getAccelerometerYDescription();
-            case TAG_CAMERA_ORIENTATION:
-                return getCameraOrientationDescription();
-            case TAG_ROLL_ANGLE:
-                return getRollAngleDescription();
-            case TAG_PITCH_ANGLE:
-                return getPitchAngleDescription();
-            case TAG_SWEEP_PANORAMA_DIRECTION:
-                return getSweepPanoramaDirectionDescription();
-            case TAG_TIMER_RECORDING:
-                return getTimerRecordingDescription();
-            case TAG_HDR:
-                return getHDRDescription();
-            case TAG_SHUTTER_TYPE:
-                return getShutterTypeDescription();
-            case TAG_TOUCH_AE:
-                return getTouchAeDescription();
-
-            case TAG_BABY_AGE:
-                return getBabyAgeDescription();
-            case TAG_BABY_AGE_1:
-                return getBabyAge1Description();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getTextStampDescription()
-    {
-        return getIndexedDescription(TAG_TEXT_STAMP, 1, "Off", "On");
-    }
-
-	@Nullable
-    public String getTextStamp1Description()
-    {
-        return getIndexedDescription(TAG_TEXT_STAMP_1, 1, "Off", "On");
-    }
-
-	@Nullable
-    public String getTextStamp2Description()
-    {
-        return getIndexedDescription(TAG_TEXT_STAMP_2, 1, "Off", "On");
-    }
-
-	@Nullable
-    public String getTextStamp3Description()
-    {
-        return getIndexedDescription(TAG_TEXT_STAMP_3, 1, "Off", "On");
-    }
-
-	@Nullable
-    public String getMacroModeDescription()
-    {
-        return getIndexedDescription(TAG_MACRO_MODE, 1, "Off", "On");
-    }
-
-    @Nullable
-    public String getFlashFiredDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_FIRED, 1, "Off", "On");
-    }
-
-    @Nullable
-    public String getImageStabilizationDescription()
-    {
-        return getIndexedDescription(TAG_IMAGE_STABILIZATION,
-            2,
-            "On, Mode 1",
-            "Off",
-            "On, Mode 2"
-        );
-    }
-
-    @Nullable
-    public String getAudioDescription()
-    {
-        return getIndexedDescription(TAG_AUDIO, 1, "Off", "On");
-    }
-
-    @Nullable
-    public String getTransformDescription()
-    {
-        return getTransformDescription(TAG_TRANSFORM);
-    }
-
-    @Nullable
-    public String getTransform1Description()
-    {
-        return getTransformDescription(TAG_TRANSFORM_1);
-    }
-
-    @Nullable
-    private String getTransformDescription(int tag)
-    {
-        byte[] values = _directory.getByteArray(tag);
-        if (values == null)
-            return null;
-
-        RandomAccessReader reader = new ByteArrayReader(values);
-
-        try
-        {
-            int val1 = reader.getUInt16(0);
-            int val2 = reader.getUInt16(2);
-
-            if (val1 == -1 && val2 == 1)
-                return "Slim Low";
-            if (val1 == -3 && val2 == 2)
-                return "Slim High";
-            if (val1 == 0 && val2 == 0)
-                return "Off";
-            if (val1 == 1 && val2 == 1)
-                return "Stretch Low";
-            if (val1 == 3 && val2 == 2)
-                return "Stretch High";
-
-            return "Unknown (" + val1 + " " + val2 + ")";
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getIntelligentExposureDescription()
-    {
-        return getIndexedDescription(TAG_INTELLIGENT_EXPOSURE,
-            "Off", "Low", "Standard", "High");
-    }
-
-    @Nullable
-    public String getFlashWarningDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_WARNING,
-            "No", "Yes (Flash required but disabled)");
-    }
-
-    @Nullable
-    private static String trim(@Nullable String s)
-    {
-        return s == null ? null : s.trim();
-    }
-
-    @Nullable
-    public String getCountryDescription()
-    {
-        return trim(getStringFromBytes(TAG_COUNTRY, Charsets.UTF_8));
-    }
-
-    @Nullable
-    public String getStateDescription()
-    {
-        return trim(getStringFromBytes(TAG_STATE, Charsets.UTF_8));
-    }
-
-    @Nullable
-    public String getCityDescription()
-    {
-        return trim(getStringFromBytes(TAG_CITY, Charsets.UTF_8));
-    }
-
-    @Nullable
-    public String getLandmarkDescription()
-    {
-        return trim(getStringFromBytes(TAG_LANDMARK, Charsets.UTF_8));
-    }
-
-    @Nullable
-    public String getTitleDescription()
-    {
-        return trim(getStringFromBytes(TAG_TITLE, Charsets.UTF_8));
-    }
-
-    @Nullable
-    public String getBracketSettingsDescription()
-    {
-        return getIndexedDescription(TAG_BRACKET_SETTINGS,
-            "No Bracket", "3 Images, Sequence 0/-/+", "3 Images, Sequence -/0/+", "5 Images, Sequence 0/-/+",
-            "5 Images, Sequence -/0/+", "7 Images, Sequence 0/-/+", "7 Images, Sequence -/0/+");
-    }
-
-    @Nullable
-    public String getFlashCurtainDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_CURTAIN,
-            "n/a", "1st", "2nd");
-    }
-
-    @Nullable
-    public String getLongExposureNoiseReductionDescription()
-    {
-        return getIndexedDescription(TAG_LONG_EXPOSURE_NOISE_REDUCTION, 1,
-            "Off", "On");
-    }
-
-    @Nullable
-    public String getLensFirmwareVersionDescription()
-    {
-        // lens version has 4 parts separated by periods
-        byte[] bytes = _directory.getByteArray(TAG_LENS_FIRMWARE_VERSION);
-        if (bytes == null)
-            return null;
-
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < bytes.length; i++) {
-            sb.append(bytes[i]);
-            if (i < bytes.length - 1)
-                sb.append(".");
-        }
-        return sb.toString();
-        //return string.Join(".", bytes.Select(b => b.ToString()).ToArray());
-    }
-
-    @Nullable
-    public String getIntelligentDRangeDescription()
-    {
-        return getIndexedDescription(TAG_INTELLIGENT_D_RANGE,
-            "Off", "Low", "Standard", "High");
-    }
-
-    @Nullable
-    public String getClearRetouchDescription()
-    {
-        return getIndexedDescription(TAG_CLEAR_RETOUCH,
-                "Off", "On");
-
-    }
-
-    @Nullable
-    public String getPhotoStyleDescription()
-    {
-        return getIndexedDescription(TAG_PHOTO_STYLE,
-            "Auto", "Standard or Custom", "Vivid", "Natural", "Monochrome", "Scenery", "Portrait");
-    }
-
-    @Nullable
-    public String getShadingCompensationDescription()
-    {
-        return getIndexedDescription(TAG_SHADING_COMPENSATION,
-            "Off", "On");
-    }
-
-    @Nullable
-    public String getAccelerometerZDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ACCELEROMETER_Z);
-        if (value == null)
-            return null;
-
-        // positive is acceleration upwards
-        return String.valueOf(value.shortValue());
-    }
-
-    @Nullable
-    public String getAccelerometerXDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ACCELEROMETER_X);
-        if (value == null)
-            return null;
-
-        // positive is acceleration to the left
-        return String.valueOf(value.shortValue());
-    }
-
-    @Nullable
-    public String getAccelerometerYDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ACCELEROMETER_Y);
-        if (value == null)
-            return null;
-
-        // positive is acceleration backwards
-        return String.valueOf(value.shortValue());
-    }
-
-    @Nullable
-    public String getCameraOrientationDescription()
-    {
-        return getIndexedDescription(TAG_CAMERA_ORIENTATION,
-                "Normal", "Rotate CW", "Rotate 180", "Rotate CCW", "Tilt Upwards", "Tile Downwards");
-    }
-
-    @Nullable
-    public String getRollAngleDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ROLL_ANGLE);
-        if (value == null)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("0.#");
-        // converted to degrees of clockwise camera rotation
-        return format.format(value.shortValue() / 10.0);
-    }
-
-    @Nullable
-    public String getPitchAngleDescription()
-    {
-        Integer value = _directory.getInteger(TAG_PITCH_ANGLE);
-        if (value == null)
-            return null;
-
-        DecimalFormat format = new DecimalFormat("0.#");
-        // converted to degrees of upward camera tilt
-        return format.format(-value.shortValue() / 10.0);
-    }
-
-    @Nullable
-    public String getSweepPanoramaDirectionDescription()
-    {
-        return getIndexedDescription(TAG_SWEEP_PANORAMA_DIRECTION,
-                "Off", "Left to Right", "Right to Left", "Top to Bottom", "Bottom to Top");
-    }
-
-    @Nullable
-    public String getTimerRecordingDescription()
-    {
-        return getIndexedDescription(TAG_TIMER_RECORDING,
-                "Off", "Time Lapse", "Stop-motion Animation");
-    }
-
-    @Nullable
-    public String getHDRDescription()
-    {
-        Integer value = _directory.getInteger(TAG_HDR);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 0:
-                return "Off";
-            case 100:
-                return "1 EV";
-            case 200:
-                return "2 EV";
-            case 300:
-                return "3 EV";
-            case 32868:
-                return "1 EV (Auto)";
-            case 32968:
-                return "2 EV (Auto)";
-            case 33068:
-                return "3 EV (Auto)";
-            default:
-                return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getShutterTypeDescription()
-    {
-        return getIndexedDescription(TAG_SHUTTER_TYPE,
-                "Mechanical", "Electronic", "Hybrid");
-    }
-
-    @Nullable
-    public String getTouchAeDescription()
-    {
-        return getIndexedDescription(TAG_TOUCH_AE,
-                "Off", "On");
-    }
-
-    @Nullable
-    public String getBabyNameDescription()
-    {
-        return trim(getStringFromBytes(TAG_BABY_NAME, Charsets.UTF_8));
-    }
-
-	@Nullable
-    public String getLocationDescription()
-    {
-        return trim(getStringFromBytes(TAG_LOCATION, Charsets.UTF_8));
-    }
-
-    @Nullable
-    public String getIntelligentResolutionDescription()
-    {
-        return getIndexedDescription(TAG_INTELLIGENT_RESOLUTION,
-            "Off", null, "Auto", "On");
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        return getIndexedDescription(TAG_CONTRAST, "Normal");
-    }
-
-    @Nullable
-    public String getWorldTimeLocationDescription()
-    {
-        return getIndexedDescription(TAG_WORLD_TIME_LOCATION,
-            1, "Home", "Destination");
-    }
-
-    @Nullable
-    public String getAdvancedSceneModeDescription()
-    {
-        return getIndexedDescription(TAG_ADVANCED_SCENE_MODE,
-            1,
-            "Normal",
-            "Outdoor/Illuminations/Flower/HDR Art",
-            "Indoor/Architecture/Objects/HDR B&W",
-            "Creative",
-            "Auto",
-            null,
-            "Expressive",
-            "Retro",
-            "Pure",
-            "Elegant",
-            null,
-            "Monochrome",
-            "Dynamic Art",
-            "Silhouette"
-        );
-    }
-
-    @Nullable
-    public String getUnknownDataDumpDescription()
-    {
-        return getByteLengthDescription(TAG_UNKNOWN_DATA_DUMP);
-    }
-
-    @Nullable
-    public String getColorEffectDescription()
-    {
-        return getIndexedDescription(TAG_COLOR_EFFECT,
-            1, "Off", "Warm", "Cool", "Black & White", "Sepia"
-        );
-    }
-
-    @Nullable
-    public String getUptimeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_UPTIME);
-        if (value == null)
-            return null;
-        return value / 100f + " s";
-    }
-
-    @Nullable
-    public String getBurstModeDescription()
-    {
-        return getIndexedDescription(TAG_BURST_MODE,
-            "Off", null, "On", "Indefinite", "Unlimited"
-        );
-    }
-
-    @Nullable
-    public String getContrastModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_CONTRAST_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x0: return "Normal";
-            case 0x1: return "Low";
-            case 0x2: return "High";
-            case 0x6: return "Medium Low";
-            case 0x7: return "Medium High";
-            case 0x100: return "Low";
-            case 0x110: return "Normal";
-            case 0x120: return "High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getNoiseReductionDescription()
-    {
-        return getIndexedDescription(TAG_NOISE_REDUCTION,
-            "Standard (0)", "Low (-1)", "High (+1)", "Lowest (-2)", "Highest (+2)"
-        );
-    }
-
-    @Nullable
-    public String getSelfTimerDescription()
-    {
-        return getIndexedDescription(TAG_SELF_TIMER,
-            1, "Off", "10 s", "2 s"
-        );
-    }
-
-    @Nullable
-    public String getRotationDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ROTATION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 1: return "Horizontal";
-            case 3: return "Rotate 180";
-            case 6: return "Rotate 90 CW";
-            case 8: return "Rotate 270 CW";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAfAssistLampDescription()
-    {
-        return getIndexedDescription(TAG_AF_ASSIST_LAMP,
-            1, "Fired", "Enabled but not used", "Disabled but required", "Disabled and not required"
-        );
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        return getIndexedDescription(TAG_COLOR_MODE,
-            "Normal", "Natural", "Vivid"
-        );
-    }
-
-    @Nullable
-    public String getOpticalZoomModeDescription()
-    {
-        return getIndexedDescription(TAG_OPTICAL_ZOOM_MODE,
-            1, "Standard", "Extended"
-        );
-    }
-
-    @Nullable
-    public String getConversionLensDescription()
-    {
-        return getIndexedDescription(TAG_CONVERSION_LENS,
-            1, "Off", "Wide", "Telephoto", "Macro"
-        );
-    }
-
-    @Nullable
-    public String getDetectedFacesDescription()
-    {
-        return buildFacesDescription(_directory.getDetectedFaces());
-    }
-
-    @Nullable
-    public String getRecognizedFacesDescription()
-    {
-        return buildFacesDescription(_directory.getRecognizedFaces());
-    }
-
-    @Nullable
-    private String buildFacesDescription(@Nullable Face[] faces)
-    {
-        if (faces == null)
-            return null;
-
-        StringBuilder result = new StringBuilder();
-
-        for (int i = 0; i < faces.length; i++)
-            result.append("Face ").append(i + 1).append(": ").append(faces[i].toString()).append("\n");
-
-        return result.length() > 0 ? result.substring(0, result.length() - 1) : null;
-
-    }
-
-    private static final String[] _sceneModes = new String[] {
-        "Normal", // 1
-        "Portrait",
-        "Scenery",
-        "Sports",
-        "Night Portrait",
-        "Program",
-        "Aperture Priority",
-        "Shutter Priority",
-        "Macro",
-        "Spot", // 10
-        "Manual",
-        "Movie Preview",
-        "Panning",
-        "Simple",
-        "Color Effects",
-        "Self Portrait",
-        "Economy",
-        "Fireworks",
-        "Party",
-        "Snow", // 20
-        "Night Scenery",
-        "Food",
-        "Baby",
-        "Soft Skin",
-        "Candlelight",
-        "Starry Night",
-        "High Sensitivity",
-        "Panorama Assist",
-        "Underwater",
-        "Beach", // 30
-        "Aerial Photo",
-        "Sunset",
-        "Pet",
-        "Intelligent ISO",
-        "Clipboard",
-        "High Speed Continuous Shooting",
-        "Intelligent Auto",
-        null,
-        "Multi-aspect",
-        null, // 40
-        "Transform",
-        "Flash Burst",
-        "Pin Hole",
-        "Film Grain",
-        "My Color",
-        "Photo Frame",
-        null,
-        null,
-        null,
-        null, // 50
-        "HDR"
-    };
-
-    @Nullable
-    public String getRecordModeDescription()
-    {
-        return getIndexedDescription(TAG_RECORD_MODE, 1, _sceneModes);
-    }
-
-    @Nullable
-    public String getSceneModeDescription()
-    {
-        return getIndexedDescription(TAG_SCENE_MODE, 1, _sceneModes);
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE, 1,
-            "Auto", "Manual", null, "Auto, Focus Button", "Auto, Continuous");
-    }
-
-    @Nullable
-    public String getAfAreaModeDescription()
-    {
-        int[] value = _directory.getIntArray(TAG_AF_AREA_MODE);
-        if (value == null || value.length < 2)
-            return null;
-        switch (value[0]) {
-            case 0:
-                switch (value[1]) {
-                    case 1: return "Spot Mode On";
-                    case 16: return "Spot Mode Off";
-                    default: return "Unknown (" + value[0] + " " + value[1] + ")";
-                }
-            case 1:
-                switch (value[1]) {
-                    case 0: return "Spot Focusing";
-                    case 1: return "5-area";
-                    default: return "Unknown (" + value[0] + " " + value[1] + ")";
-                }
-            case 16:
-                switch (value[1]) {
-                    case 0: return "1-area";
-                    case 16: return "1-area (high speed)";
-                    default: return "Unknown (" + value[0] + " " + value[1] + ")";
-                }
-            case 32:
-                switch (value[1]) {
-                    case 0: return "Auto or Face Detect";
-                    case 1: return "3-area (left)";
-                    case 2: return "3-area (center)";
-                    case 3: return "3-area (right)";
-                    default: return "Unknown (" + value[0] + " " + value[1] + ")";
-                }
-            case 64: return "Face Detect";
-            default: return "Unknown (" + value[0] + " " + value[1] + ")";
-        }
-    }
-
-    @Nullable
-    public String getQualityModeDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY_MODE,
-            2,
-            "High", // 2
-            "Normal",
-            null,
-            null,
-            "Very High",
-            "Raw",
-            null,
-            "Motion Picture" // 9
-        );
-    }
-
-    @Nullable
-    public String getVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_FIRMWARE_VERSION, 2);
-    }
-
-    @Nullable
-    public String getMakernoteVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_MAKERNOTE_VERSION, 2);
-    }
-
-    @Nullable
-    public String getExifVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_EXIF_VERSION, 2);
-    }
-
-    @Nullable
-    public String getInternalSerialNumberDescription()
-    {
-        return get7BitStringFromBytes(TAG_INTERNAL_SERIAL_NUMBER);
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(TAG_WHITE_BALANCE,
-            1,
-            "Auto", // 1
-            "Daylight",
-            "Cloudy",
-            "Incandescent",
-            "Manual",
-            null,
-            null,
-            "Flash",
-            null,
-            "Black & White", // 10
-            "Manual",
-            "Shade" // 12
-        );
-    }
-
-	@Nullable
-	public String getBabyAgeDescription()
-    {
-        final Age age = _directory.getAge(TAG_BABY_AGE);
-        return age == null ? null : age.toFriendlyString();
-    }
-
-	@Nullable
-	public String getBabyAge1Description()
-    {
-        final Age age = _directory.getAge(TAG_BABY_AGE_1);
-        return age == null ? null : age.toFriendlyString();
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/PanasonicMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,711 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Age;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Face;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Panasonic and Leica cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- * @author Philipp Sandhaus
- */
-@SuppressWarnings("WeakerAccess")
-public class PanasonicMakernoteDirectory extends Directory
-{
-
-    /**
-     * <br>
-     * 2 = High            <br>
-     * 3 = Normal          <br>
-     * 6 = Very High       <br>
-     * 7 = Raw             <br>
-     * 9 = Motion Picture  <br>
-     */
-    public static final int TAG_QUALITY_MODE = 0x0001;
-    public static final int TAG_FIRMWARE_VERSION = 0x0002;
-
-    /**
-     * <br>
-     * 1 = Auto            <br>
-     * 2 = Daylight        <br>
-     * 3 = Cloudy          <br>
-     * 4 = Incandescent    <br>
-     * 5 = Manual          <br>
-     * 8 = Flash           <br>
-     * 10 = Black &amp; White  <br>
-     * 11 = Manual         <br>
-     * 12 = Shade          <br>
-     */
-    public static final int TAG_WHITE_BALANCE = 0x0003;
-
-
-    /**
-     * <br>
-     * 1 = Auto                <br>
-     * 2 = Manual              <br>
-     * 4 =  Auto, Focus Button <br>
-     * 5 = Auto, Continuous    <br>
-     */
-    public static final int TAG_FOCUS_MODE = 0x0007;
-
-    /**
-     * <br>
-     * 2 bytes                         <br>
-     * (DMC-FZ10)                      <br>
-     * '0 1' = Spot Mode On            <br>
-     * '0 16' = Spot Mode Off          <br>
-     * '(other models)                 <br>
-     * 16 = Normal?                    <br>
-     * '0 1' = 9-area                  <br>
-     * '0 16' = 3-area (high speed)    <br>
-     * '1 0' = Spot Focusing           <br>
-     * '1 1' = 5-area                  <br>
-     * '16 0' = 1-area                 <br>
-     * '16 16' = 1-area (high speed)   <br>
-     * '32 0' = Auto or Face Detect    <br>
-     * '32 1' = 3-area (left)?         <br>
-     * '32 2' = 3-area (center)?       <br>
-     * '32 3' = 3-area (right)?        <br>
-     * '64 0' = Face Detect            <br>
-     */
-    public static final int TAG_AF_AREA_MODE = 0x000f;
-
-    /**
-     * <br>
-     * 2 = On, Mode 1   <br>
-     * 3 = Off          <br>
-     * 4 = On, Mode 2   <br>
-     */
-    public static final int TAG_IMAGE_STABILIZATION = 0x001a;
-
-    /**
-     * <br>
-     * 1 = On    <br>
-     * 2 = Off   <br>
-     */
-    public static final int TAG_MACRO_MODE = 0x001C;
-
-    /**
-     * <br>
-     * 1 = Normal                            <br>
-     * 2 = Portrait                          <br>
-     * 3 = Scenery                           <br>
-     * 4 = Sports                            <br>
-     * 5 = Night Portrait                    <br>
-     * 6 = Program                           <br>
-     * 7 = Aperture Priority                 <br>
-     * 8 = Shutter Priority                  <br>
-     * 9 = Macro                             <br>
-     * 10= Spot                              <br>
-     * 11= Manual                            <br>
-     * 12= Movie Preview                     <br>
-     * 13= Panning                           <br>
-     * 14= Simple                            <br>
-     * 15= Color Effects                     <br>
-     * 16= Self Portrait                     <br>
-     * 17= Economy                           <br>
-     * 18= Fireworks                         <br>
-     * 19= Party                             <br>
-     * 20= Snow                              <br>
-     * 21= Night Scenery                     <br>
-     * 22= Food                              <br>
-     * 23= Baby                              <br>
-     * 24= Soft Skin                         <br>
-     * 25= Candlelight                       <br>
-     * 26= Starry Night                      <br>
-     * 27= High Sensitivity                  <br>
-     * 28= Panorama Assist                   <br>
-     * 29= Underwater                        <br>
-     * 30= Beach                             <br>
-     * 31= Aerial Photo                      <br>
-     * 32= Sunset                            <br>
-     * 33= Pet                               <br>
-     * 34= Intelligent ISO                   <br>
-     * 35= Clipboard                         <br>
-     * 36= High Speed Continuous Shooting    <br>
-     * 37= Intelligent Auto                  <br>
-     * 39= Multi-aspect                      <br>
-     * 41= Transform                         <br>
-     * 42= Flash Burst                       <br>
-     * 43= Pin Hole                          <br>
-     * 44= Film Grain                        <br>
-     * 45= My Color                          <br>
-     * 46= Photo Frame                       <br>
-     * 51= HDR                               <br>
-     */
-    public static final int TAG_RECORD_MODE = 0x001F;
-
-    /**
-     * 1 = Yes <br>
-     * 2 = No  <br>
-     */
-    public static final int TAG_AUDIO = 0x0020;
-
-    /**
-     * No idea, what this is
-     */
-    public static final int TAG_UNKNOWN_DATA_DUMP = 0x0021;
-
-    public static final int TAG_EASY_MODE = 0x0022;
-    public static final int TAG_WHITE_BALANCE_BIAS = 0x0023;
-    public static final int TAG_FLASH_BIAS = 0x0024;
-
-    /**
-     * this number is unique, and contains the date of manufacture,
-     * but is not the same as the number printed on the camera body
-     */
-    public static final int TAG_INTERNAL_SERIAL_NUMBER = 0x0025;
-
-    /**
-     * Panasonic Exif Version
-     */
-    public static final int TAG_EXIF_VERSION = 0x0026;
-
-
-    /**
-     * 1 = Off           <br>
-     * 2 = Warm          <br>
-     * 3 = Cool          <br>
-     * 4 = Black &amp; White <br>
-     * 5 = Sepia         <br>
-     */
-    public static final int TAG_COLOR_EFFECT = 0x0028;
-
-    /**
-     * 4 Bytes <br>
-     * Time in 1/100 s from when the camera was powered on to when the
-     * image is written to memory card
-     */
-    public static final int TAG_UPTIME = 0x0029;
-
-
-    /**
-     * 0 = Off        <br>
-     * 1 = On         <br>
-     * 2 = Infinite   <br>
-     * 4 = Unlimited  <br>
-     */
-    public static final int TAG_BURST_MODE = 0x002a;
-
-    public static final int TAG_SEQUENCE_NUMBER = 0x002b;
-
-    /**
-     * (this decoding seems to work for some models such as the LC1, LX2, FZ7, FZ8, FZ18 and FZ50, but may not be correct for other models such as the FX10, G1, L1, L10 and LC80) <br>
-     * 0x0 = Normal                                            <br>
-     * 0x1 = Low                                               <br>
-     * 0x2 = High                                              <br>
-     * 0x6 = Medium Low                                        <br>
-     * 0x7 = Medium High                                       <br>
-     * 0x100 = Low                                             <br>
-     * 0x110 = Normal                                          <br>
-     * 0x120 = High                                            <br>
-     * (these values are used by the GF1)                      <br>
-     * 0 = -2                                                  <br>
-     * 1 = -1                                                  <br>
-     * 2 = Normal                                              <br>
-     * 3 = +1                                                  <br>
-     * 4 = +2                                                  <br>
-     * 7 = Nature (Color Film)                                 <br>
-     * 12 = Smooth (Color Film) or Pure (My Color)             <br>
-     * 17 = Dynamic (B&amp;W Film)                                 <br>
-     * 22 = Smooth (B&amp;W Film)                                  <br>
-     * 27 = Dynamic (Color Film)                               <br>
-     * 32 = Vibrant (Color Film) or Expressive (My Color)      <br>
-     * 33 = Elegant (My Color)                                 <br>
-     * 37 = Nostalgic (Color Film)                             <br>
-     * 41 = Dynamic Art (My Color)                             <br>
-     * 42 = Retro (My Color)                                   <br>
-     */
-    public static final int TAG_CONTRAST_MODE = 0x002c;
-
-
-    /**
-     * 0 = Standard      <br>
-     * 1 = Low (-1)      <br>
-     * 2 = High (+1)     <br>
-     * 3 = Lowest (-2)   <br>
-     * 4 = Highest (+2)  <br>
-     */
-    public static final int TAG_NOISE_REDUCTION = 0x002d;
-
-    /**
-     * 1 = Off   <br>
-     * 2 = 10 s  <br>
-     * 3 = 2 s   <br>
-     */
-    public static final int TAG_SELF_TIMER = 0x002e;
-
-    /**
-     * 1 = 0 DG    <br>
-     * 3 = 180 DG  <br>
-     * 6 =  90 DG  <br>
-     * 8 = 270 DG  <br>
-     */
-    public static final int TAG_ROTATION = 0x0030;
-
-    /**
-     * 1 = Fired <br>
-     * 2 = Enabled nut not used <br>
-     * 3 = Disabled but required <br>
-     * 4 = Disabled and not required
-     */
-    public static final int TAG_AF_ASSIST_LAMP = 0x0031;
-
-    /**
-     * 0 = Normal <br>
-     * 1 = Natural<br>
-     * 2 = Vivid
-     *
-     */
-    public static final int TAG_COLOR_MODE = 0x0032;
-
-    public static final int TAG_BABY_AGE = 0x0033;
-
-    /**
-     *  1 = Standard <br>
-     *  2 = Extended
-     */
-    public static final int TAG_OPTICAL_ZOOM_MODE = 0x0034;
-
-    /**
-     * 1 = Off <br>
-     * 2 = Wide <br>
-     * 3 = Telephoto <br>
-     * 4 = Macro
-     */
-    public static final int TAG_CONVERSION_LENS = 0x0035;
-
-    public static final int TAG_TRAVEL_DAY = 0x0036;
-
-    /**
-     * 0 = Normal
-     */
-    public static final int TAG_CONTRAST = 0x0039;
-
-    /**
-     * <br>
-     * 1 = Home <br>
-     * 2 = Destination
-     */
-    public static final int TAG_WORLD_TIME_LOCATION = 0x003a;
-
-    /**
-     * 1 = Off   <br>
-     * 2 = On
-     */
-    public static final int TAG_TEXT_STAMP = 0x003b;
-
-	public static final int TAG_PROGRAM_ISO = 0x003c;
-
-    /**
-     * <br>
-     * 1 = Normal                               <br>
-     * 2 = Outdoor/Illuminations/Flower/HDR Art <br>
-     * 3 = Indoor/Architecture/Objects/HDR B&amp;W  <br>
-     * 4 = Creative                             <br>
-     * 5 = Auto                                 <br>
-     * 7 = Expressive                           <br>
-     * 8 = Retro                                <br>
-     * 9 = Pure                                 <br>
-     * 10 = Elegant                             <br>
-     * 12 = Monochrome                          <br>
-     * 13 = Dynamic Art                         <br>
-     * 14 = Silhouette                          <br>
-     */
-    public static final int TAG_ADVANCED_SCENE_MODE = 0x003d;
-
-    /**
-     * 1 = Off   <br>
-     * 2 = On
-     */
-    public static final int TAG_TEXT_STAMP_1 = 0x003e;
-
-    public static final int TAG_FACES_DETECTED = 0x003f;
-
-    public static final int TAG_SATURATION = 0x0040;
-    public static final int TAG_SHARPNESS = 0x0041;
-    public static final int TAG_FILM_MODE = 0x0042;
-
-    public static final int TAG_COLOR_TEMP_KELVIN = 0x0044;
-    public static final int TAG_BRACKET_SETTINGS = 0x0045;
-
-    /**
-    * WB adjust AB. Positive is a shift toward blue.
-    */
-    public static final int TAG_WB_ADJUST_AB = 0x0046;
-    /**
-    * WB adjust GM. Positive is a shift toward green.
-    */
-    public static final int TAG_WB_ADJUST_GM = 0x0047;
-
-    public static final int TAG_FLASH_CURTAIN = 0x0048;
-    public static final int TAG_LONG_EXPOSURE_NOISE_REDUCTION = 0x0049;
-
-    public static final int TAG_PANASONIC_IMAGE_WIDTH = 0x004b;
-    public static final int TAG_PANASONIC_IMAGE_HEIGHT = 0x004c;
-    public static final int TAG_AF_POINT_POSITION = 0x004d;
-
-
-    /**
-     * <br>
-     * Integer (16Bit) Indexes:                                             <br>
-     * 0  Number Face Positions (maybe less than Faces Detected)            <br>
-     * 1-4 Face Position 1                                                  <br>
-     * 5-8 Face Position 2                                                  <br>
-     * and so on                                                            <br>
-     *                                                                      <br>
-     * The four Integers are interpreted as follows:                        <br>
-     * (XYWH)  X,Y Center of Face,  (W,H) Width and Height                  <br>
-     * All values are in respect to double the size of the thumbnail image  <br>
-     *
-     */
-    public static final int TAG_FACE_DETECTION_INFO = 0x004e;
-    public static final int TAG_LENS_TYPE = 0x0051;
-    public static final int TAG_LENS_SERIAL_NUMBER = 0x0052;
-    public static final int TAG_ACCESSORY_TYPE = 0x0053;
-    public static final int TAG_ACCESSORY_SERIAL_NUMBER = 0x0054;
-
-    /**
-     * (decoded as two 16-bit signed integers)
-     * '-1 1' = Slim Low
-     * '-3 2' = Slim High
-     * '0 0' = Off
-     * '1 1' = Stretch Low
-     * '3 2' = Stretch High
-     */
-    public static final int TAG_TRANSFORM = 0x0059;
-
-    /**
-    * 0 = Off <br>
-    * 1 = Low <br>
-    * 2 = Standard <br>
-    * 3 = High
-    */
-    public static final int TAG_INTELLIGENT_EXPOSURE = 0x005d;
-
-    public static final int TAG_LENS_FIRMWARE_VERSION = 0x0060;
-    public static final int TAG_BURST_SPEED = 0x0077;
-    public static final int TAG_INTELLIGENT_D_RANGE = 0x0079;
-    public static final int TAG_CLEAR_RETOUCH = 0x007c;
-    public static final int TAG_CITY2 = 0x0080;
-    public static final int TAG_PHOTO_STYLE = 0x0089;
-    public static final int TAG_SHADING_COMPENSATION = 0x008a;
-
-    public static final int TAG_ACCELEROMETER_Z = 0x008c;
-    public static final int TAG_ACCELEROMETER_X = 0x008d;
-    public static final int TAG_ACCELEROMETER_Y = 0x008e;
-    public static final int TAG_CAMERA_ORIENTATION = 0x008f;
-    public static final int TAG_ROLL_ANGLE = 0x0090;
-    public static final int TAG_PITCH_ANGLE = 0x0091;
-    public static final int TAG_SWEEP_PANORAMA_DIRECTION = 0x0093;
-    public static final int TAG_SWEEP_PANORAMA_FIELD_OF_VIEW = 0x0094;
-    public static final int TAG_TIMER_RECORDING = 0x0096;
-
-    public static final int TAG_INTERNAL_ND_FILTER = 0x009d;
-    public static final int TAG_HDR = 0x009e;
-    public static final int TAG_SHUTTER_TYPE = 0x009f;
-
-    public static final int TAG_CLEAR_RETOUCH_VALUE = 0x00a3;
-    public static final int TAG_TOUCH_AE = 0x00ab;
-
-    /**
-    * Info at http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
-    */
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    /**
-     * Byte Indexes:                                                                       <br>
-     *  0    Int (2  Byte) Number of Recognized Faces                                      <br>
-     *  4    String(20 Byte)    Recognized Face 1 Name                                     <br>
-     * 24    4 Int (8 Byte)     Recognized Face 1 Position  (Same Format as Face Detection)  <br>
-     * 32    String(20 Byte)    Recognized Face 1 Age                                      <br>
-     * 52    String(20 Byte)    Recognized Face 2 Name                                     <br>
-     * 72    4 Int (8 Byte)     Recognized Face 2 Position  (Same Format as Face Detection)  <br>
-     * 80    String(20 Byte)    Recognized Face 2 Age                                      <br>
-     *                                                                                     <br>
-     * And so on                                                                           <br>
-     *                                                                                     <br>
-     * The four Integers are interpreted as follows:                                       <br>
-     * (XYWH)  X,Y Center of Face,  (W,H) Width and Height                                 <br>
-     * All values are in respect to double the size of the thumbnail image                 <br>
-     *
-     */
-    public static final int TAG_FACE_RECOGNITION_INFO = 0x0061;
-
-    /**
-    * 0 = No <br>
-    * 1 = Yes
-    */
-    public static final int TAG_FLASH_WARNING = 0x0062;
-    public static final int TAG_RECOGNIZED_FACE_FLAGS = 0x0063;
-    public static final int TAG_TITLE = 0x0065;
-    public static final int TAG_BABY_NAME = 0x0066;
-    public static final int TAG_LOCATION = 0x0067;
-    public static final int TAG_COUNTRY = 0x0069;
-    public static final int TAG_STATE = 0x006b;
-    public static final int TAG_CITY = 0x006d;
-    public static final int TAG_LANDMARK = 0x006f;
-
-    /**
-     * 0 = Off <br>
-     * 2 = Auto <br>
-     * 3 = On
-     */
-    public static final int TAG_INTELLIGENT_RESOLUTION = 0x0070;
-
-    public static final int TAG_MAKERNOTE_VERSION = 0x8000;
-    public static final int TAG_SCENE_MODE = 0x8001;
-    public static final int TAG_WB_RED_LEVEL = 0x8004;
-    public static final int TAG_WB_GREEN_LEVEL = 0x8005;
-    public static final int TAG_WB_BLUE_LEVEL = 0x8006;
-    public static final int TAG_FLASH_FIRED = 0x8007;
-    public static final int TAG_TEXT_STAMP_2 = 0x8008;
-    public static final int TAG_TEXT_STAMP_3 = 0x8009;
-    public static final int TAG_BABY_AGE_1 = 0x8010;
-
-	/**
-     * (decoded as two 16-bit signed integers)
-     * '-1 1' = Slim Low
-     * '-3 2' = Slim High
-     * '0 0' = Off
-     * '1 1' = Stretch Low
-     * '3 2' = Stretch High
-     */
-    public static final int TAG_TRANSFORM_1 = 0x8012;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_QUALITY_MODE, "Quality Mode");
-        _tagNameMap.put(TAG_FIRMWARE_VERSION, "Version");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(TAG_AF_AREA_MODE, "AF Area Mode");
-        _tagNameMap.put(TAG_IMAGE_STABILIZATION, "Image Stabilization");
-        _tagNameMap.put(TAG_MACRO_MODE, "Macro Mode");
-        _tagNameMap.put(TAG_RECORD_MODE, "Record Mode");
-        _tagNameMap.put(TAG_AUDIO, "Audio");
-        _tagNameMap.put(TAG_INTERNAL_SERIAL_NUMBER, "Internal Serial Number");
-        _tagNameMap.put(TAG_UNKNOWN_DATA_DUMP, "Unknown Data Dump");
-        _tagNameMap.put(TAG_EASY_MODE, "Easy Mode");
-        _tagNameMap.put(TAG_WHITE_BALANCE_BIAS, "White Balance Bias");
-        _tagNameMap.put(TAG_FLASH_BIAS, "Flash Bias");
-        _tagNameMap.put(TAG_EXIF_VERSION, "Exif Version");
-        _tagNameMap.put(TAG_COLOR_EFFECT, "Color Effect");
-        _tagNameMap.put(TAG_UPTIME, "Camera Uptime");
-        _tagNameMap.put(TAG_BURST_MODE, "Burst Mode");
-        _tagNameMap.put(TAG_SEQUENCE_NUMBER, "Sequence Number");
-        _tagNameMap.put(TAG_CONTRAST_MODE, "Contrast Mode");
-        _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction");
-        _tagNameMap.put(TAG_SELF_TIMER, "Self Timer");
-        _tagNameMap.put(TAG_ROTATION, "Rotation");
-        _tagNameMap.put(TAG_AF_ASSIST_LAMP, "AF Assist Lamp");
-        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
-        _tagNameMap.put(TAG_BABY_AGE, "Baby Age");
-        _tagNameMap.put(TAG_OPTICAL_ZOOM_MODE, "Optical Zoom Mode");
-        _tagNameMap.put(TAG_CONVERSION_LENS, "Conversion Lens");
-        _tagNameMap.put(TAG_TRAVEL_DAY, "Travel Day");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_WORLD_TIME_LOCATION, "World Time Location");
-        _tagNameMap.put(TAG_TEXT_STAMP, "Text Stamp");
-        _tagNameMap.put(TAG_PROGRAM_ISO, "Program ISO");
-	_tagNameMap.put(TAG_ADVANCED_SCENE_MODE, "Advanced Scene Mode");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-        _tagNameMap.put(TAG_FACES_DETECTED, "Number of Detected Faces");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_FILM_MODE, "Film Mode");
-        _tagNameMap.put(TAG_COLOR_TEMP_KELVIN, "Color Temp Kelvin");
-        _tagNameMap.put(TAG_BRACKET_SETTINGS, "Bracket Settings");
-        _tagNameMap.put(TAG_WB_ADJUST_AB, "White Balance Adjust (AB)");
-	_tagNameMap.put(TAG_WB_ADJUST_GM, "White Balance Adjust (GM)");
-
-        _tagNameMap.put(TAG_FLASH_CURTAIN, "Flash Curtain");
-        _tagNameMap.put(TAG_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction");
-        _tagNameMap.put(TAG_PANASONIC_IMAGE_WIDTH, "Panasonic Image Width");
-        _tagNameMap.put(TAG_PANASONIC_IMAGE_HEIGHT, "Panasonic Image Height");
-
-        _tagNameMap.put(TAG_AF_POINT_POSITION, "Af Point Position");
-        _tagNameMap.put(TAG_FACE_DETECTION_INFO, "Face Detection Info");
-        _tagNameMap.put(TAG_LENS_TYPE, "Lens Type");
-        _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number");
-        _tagNameMap.put(TAG_ACCESSORY_TYPE, "Accessory Type");
-        _tagNameMap.put(TAG_ACCESSORY_SERIAL_NUMBER, "Accessory Serial Number");
-        _tagNameMap.put(TAG_TRANSFORM, "Transform");
-        _tagNameMap.put(TAG_INTELLIGENT_EXPOSURE, "Intelligent Exposure");
-        _tagNameMap.put(TAG_LENS_FIRMWARE_VERSION, "Lens Firmware Version");
-        _tagNameMap.put(TAG_FACE_RECOGNITION_INFO, "Face Recognition Info");
-        _tagNameMap.put(TAG_FLASH_WARNING, "Flash Warning");
-        _tagNameMap.put(TAG_RECOGNIZED_FACE_FLAGS, "Recognized Face Flags");
-        _tagNameMap.put(TAG_TITLE, "Title");
-        _tagNameMap.put(TAG_BABY_NAME, "Baby Name");
-        _tagNameMap.put(TAG_LOCATION, "Location");
-        _tagNameMap.put(TAG_COUNTRY, "Country");
-        _tagNameMap.put(TAG_STATE, "State");
-        _tagNameMap.put(TAG_CITY, "City");
-        _tagNameMap.put(TAG_LANDMARK, "Landmark");
-        _tagNameMap.put(TAG_INTELLIGENT_RESOLUTION, "Intelligent Resolution");
-        _tagNameMap.put(TAG_BURST_SPEED, "Burst Speed");
-        _tagNameMap.put(TAG_INTELLIGENT_D_RANGE, "Intelligent D-Range");
-        _tagNameMap.put(TAG_CLEAR_RETOUCH, "Clear Retouch");
-        _tagNameMap.put(TAG_CITY2, "City 2");
-        _tagNameMap.put(TAG_PHOTO_STYLE, "Photo Style");
-        _tagNameMap.put(TAG_SHADING_COMPENSATION, "Shading Compensation");
-
-        _tagNameMap.put(TAG_ACCELEROMETER_Z, "Accelerometer Z");
-        _tagNameMap.put(TAG_ACCELEROMETER_X, "Accelerometer X");
-        _tagNameMap.put(TAG_ACCELEROMETER_Y, "Accelerometer Y");
-        _tagNameMap.put(TAG_CAMERA_ORIENTATION, "Camera Orientation");
-        _tagNameMap.put(TAG_ROLL_ANGLE, "Roll Angle");
-        _tagNameMap.put(TAG_PITCH_ANGLE, "Pitch Angle");
-        _tagNameMap.put(TAG_SWEEP_PANORAMA_DIRECTION, "Sweep Panorama Direction");
-        _tagNameMap.put(TAG_SWEEP_PANORAMA_FIELD_OF_VIEW, "Sweep Panorama Field Of View");
-        _tagNameMap.put(TAG_TIMER_RECORDING, "Timer Recording");
-
-        _tagNameMap.put(TAG_INTERNAL_ND_FILTER, "Internal ND Filter");
-        _tagNameMap.put(TAG_HDR, "HDR");
-        _tagNameMap.put(TAG_SHUTTER_TYPE, "Shutter Type");
-        _tagNameMap.put(TAG_CLEAR_RETOUCH_VALUE, "Clear Retouch Value");
-        _tagNameMap.put(TAG_TOUCH_AE, "Touch AE");
-
-        _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
-        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
-        _tagNameMap.put(TAG_WB_RED_LEVEL, "White Balance (Red)");
-        _tagNameMap.put(TAG_WB_GREEN_LEVEL, "White Balance (Green)");
-        _tagNameMap.put(TAG_WB_BLUE_LEVEL, "White Balance (Blue)");
-        _tagNameMap.put(TAG_FLASH_FIRED, "Flash Fired");
-        _tagNameMap.put(TAG_TEXT_STAMP_1, "Text Stamp 1");
-        _tagNameMap.put(TAG_TEXT_STAMP_2, "Text Stamp 2");
-        _tagNameMap.put(TAG_TEXT_STAMP_3, "Text Stamp 3");
-        _tagNameMap.put(TAG_BABY_AGE_1, "Baby Age 1");
-        _tagNameMap.put(TAG_TRANSFORM_1, "Transform 1");
-    }
-
-    public PanasonicMakernoteDirectory()
-    {
-        this.setDescriptor(new PanasonicMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Panasonic Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    @Nullable
-    public Face[] getDetectedFaces()
-    {
-        byte[] bytes = getByteArray(TAG_FACE_DETECTION_INFO);
-        if (bytes==null)
-            return null;
-
-        RandomAccessReader reader = new ByteArrayReader(bytes);
-        reader.setMotorolaByteOrder(false);
-
-        try {
-            int faceCount = reader.getUInt16(0);
-            if (faceCount==0)
-                return null;
-            Face[] faces = new Face[faceCount];
-
-            for (int i = 0; i < faceCount; i++) {
-                int offset = 2 + i * 8;
-                faces[i] = new Face(
-                        reader.getUInt16(offset),
-                        reader.getUInt16(offset + 2),
-                        reader.getUInt16(offset + 4),
-                        reader.getUInt16(offset + 6)
-                        , null, null);
-            }
-            return faces;
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public Face[] getRecognizedFaces()
-    {
-        byte[] bytes = getByteArray(TAG_FACE_RECOGNITION_INFO);
-        if (bytes == null)
-            return null;
-
-        RandomAccessReader reader = new ByteArrayReader(bytes);
-        reader.setMotorolaByteOrder(false);
-
-        try {
-            int faceCount = reader.getUInt16(0);
-            if (faceCount==0)
-                return null;
-            Face[] faces = new Face[faceCount];
-
-            for (int i = 0; i < faceCount; i++) {
-                int offset = 4 + i * 44;
-                String name = reader.getString(offset, 20, "ASCII").trim();
-                String age = reader.getString(offset + 28, 20, "ASCII").trim();
-                faces[i] = new Face(
-                        reader.getUInt16(offset + 20),
-                        reader.getUInt16(offset + 22),
-                        reader.getUInt16(offset + 24),
-                        reader.getUInt16(offset + 26),
-                        name,
-                        Age.fromPanasonicString(age));
-            }
-            return faces;
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Attempts to convert the underlying string value (as stored in the directory) into an Age object.
-     * @param tag The tag identifier.
-     * @return The parsed Age object, or null if the tag was empty of the value unable to be parsed.
-     */
-	@Nullable
-	public Age getAge(int tag)
-    {
-        final String ageString = getString(tag);
-        if (ageString==null)
-            return null;
-        return Age.fromPanasonicString(ageString);
-	}
-}
Index: unk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,160 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.PentaxMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link PentaxMakernoteDirectory}.
- * <p>
- * Some information about this makernote taken from here:
- * http://www.ozhiker.com/electronics/pjmt/jpeg_info/pentax_mn.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PentaxMakernoteDescriptor extends TagDescriptor<PentaxMakernoteDirectory>
-{
-    public PentaxMakernoteDescriptor(@NotNull PentaxMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_CAPTURE_MODE:
-                return getCaptureModeDescription();
-            case TAG_QUALITY_LEVEL:
-                return getQualityLevelDescription();
-            case TAG_FOCUS_MODE:
-                return getFocusModeDescription();
-            case TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case TAG_SHARPNESS:
-                return getSharpnessDescription();
-            case TAG_CONTRAST:
-                return getContrastDescription();
-            case TAG_SATURATION:
-                return getSaturationDescription();
-            case TAG_ISO_SPEED:
-                return getIsoSpeedDescription();
-            case TAG_COLOUR:
-                return getColourDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getColourDescription()
-    {
-        return getIndexedDescription(TAG_COLOUR, 1, "Normal", "Black & White", "Sepia");
-    }
-
-    @Nullable
-    public String getIsoSpeedDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ISO_SPEED);
-        if (value == null)
-            return null;
-        switch (value) {
-            // TODO there must be other values which aren't catered for here
-            case 10: return "ISO 100";
-            case 16: return "ISO 200";
-            case 100: return "ISO 100";
-            case 200: return "ISO 200";
-            default: return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSaturationDescription()
-    {
-        return getIndexedDescription(TAG_SATURATION, "Normal", "Low", "High");
-    }
-
-    @Nullable
-    public String getContrastDescription()
-    {
-        return getIndexedDescription(TAG_CONTRAST, "Normal", "Low", "High");
-    }
-
-    @Nullable
-    public String getSharpnessDescription()
-    {
-        return getIndexedDescription(TAG_SHARPNESS, "Normal", "Soft", "Hard");
-    }
-
-    @Nullable
-    public String getDigitalZoomDescription()
-    {
-        Float value = _directory.getFloatObject(TAG_DIGITAL_ZOOM);
-        if (value == null)
-            return null;
-        if (value == 0)
-            return "Off";
-        return Float.toString(value);
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        return getIndexedDescription(TAG_WHITE_BALANCE,
-            "Auto", "Daylight", "Shade", "Tungsten", "Fluorescent", "Manual");
-    }
-
-    @Nullable
-    public String getFlashModeDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_MODE,
-            1, "Auto", "Flash On", null, "Flash Off", null, "Red-eye Reduction");
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE, 2, "Custom", "Auto");
-    }
-
-    @Nullable
-    public String getQualityLevelDescription()
-    {
-        return getIndexedDescription(TAG_QUALITY_LEVEL, "Good", "Better", "Best");
-    }
-
-    @Nullable
-    public String getCaptureModeDescription()
-    {
-        return getIndexedDescription(TAG_CAPTURE_MODE,
-            "Auto", "Night-scene", "Manual", null, "Multiple");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/PentaxMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,171 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Pentax and Asahi cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PentaxMakernoteDirectory extends Directory
-{
-    /**
-     * 0 = Auto
-     * 1 = Night-scene
-     * 2 = Manual
-     * 4 = Multiple
-     */
-    public static final int TAG_CAPTURE_MODE = 0x0001;
-
-    /**
-     * 0 = Good
-     * 1 = Better
-     * 2 = Best
-     */
-    public static final int TAG_QUALITY_LEVEL = 0x0002;
-
-    /**
-     * 2 = Custom
-     * 3 = Auto
-     */
-    public static final int TAG_FOCUS_MODE = 0x0003;
-
-    /**
-     * 1 = Auto
-     * 2 = Flash on
-     * 4 = Flash off
-     * 6 = Red-eye Reduction
-     */
-    public static final int TAG_FLASH_MODE = 0x0004;
-
-    /**
-     * 0 = Auto
-     * 1 = Daylight
-     * 2 = Shade
-     * 3 = Tungsten
-     * 4 = Fluorescent
-     * 5 = Manual
-     */
-    public static final int TAG_WHITE_BALANCE = 0x0007;
-
-    /**
-     * (0 = Off)
-     */
-    public static final int TAG_DIGITAL_ZOOM = 0x000A;
-
-    /**
-     * 0 = Normal
-     * 1 = Soft
-     * 2 = Hard
-     */
-    public static final int TAG_SHARPNESS = 0x000B;
-
-    /**
-     * 0 = Normal
-     * 1 = Low
-     * 2 = High
-     */
-    public static final int TAG_CONTRAST = 0x000C;
-
-    /**
-     * 0 = Normal
-     * 1 = Low
-     * 2 = High
-     */
-    public static final int TAG_SATURATION = 0x000D;
-
-    /**
-     * 10 = ISO 100
-     * 16 = ISO 200
-     * 100 = ISO 100
-     * 200 = ISO 200
-     */
-    public static final int TAG_ISO_SPEED = 0x0014;
-
-    /**
-     * 1 = Normal
-     * 2 = Black &amp; White
-     * 3 = Sepia
-     */
-    public static final int TAG_COLOUR = 0x0017;
-
-    /**
-     * See Print Image Matching for specification.
-     * http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html
-     */
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    /**
-     * (String).
-     */
-    public static final int TAG_TIME_ZONE = 0x1000;
-
-    /**
-     * (String).
-     */
-    public static final int TAG_DAYLIGHT_SAVINGS = 0x1001;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_CAPTURE_MODE, "Capture Mode");
-        _tagNameMap.put(TAG_QUALITY_LEVEL, "Quality Level");
-        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_ISO_SPEED, "ISO Speed");
-        _tagNameMap.put(TAG_COLOUR, "Colour");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-        _tagNameMap.put(TAG_TIME_ZONE, "Time Zone");
-        _tagNameMap.put(TAG_DAYLIGHT_SAVINGS, "Daylight Savings");
-    }
-
-    public PentaxMakernoteDirectory()
-    {
-        this.setDescriptor(new PentaxMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Pentax Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/ReconyxHyperFireMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/ReconyxHyperFireMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,105 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.StringValue;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-
-import static com.drew.metadata.exif.makernotes.ReconyxHyperFireMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ReconyxHyperFireMakernoteDirectory}.
- *
- * @author Todd West http://cascadescarnivoreproject.blogspot.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ReconyxHyperFireMakernoteDescriptor extends TagDescriptor<ReconyxHyperFireMakernoteDirectory>
-{
-    public ReconyxHyperFireMakernoteDescriptor(@NotNull ReconyxHyperFireMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_MAKERNOTE_VERSION:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_FIRMWARE_VERSION:
-                return _directory.getString(tagType);
-            case TAG_TRIGGER_MODE:
-                return _directory.getString(tagType);
-            case TAG_SEQUENCE:
-                int[] sequence = _directory.getIntArray(tagType);
-                if (sequence == null)
-                    return null;
-                return String.format("%d/%d", sequence[0], sequence[1]);
-            case TAG_EVENT_NUMBER:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_MOTION_SENSITIVITY:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_BATTERY_VOLTAGE:
-                Double value = _directory.getDoubleObject(tagType);
-                DecimalFormat formatter = new DecimalFormat("0.000");
-                return value == null ? null : formatter.format(value);
-            case TAG_DATE_TIME_ORIGINAL:
-                String date = _directory.getString(tagType);
-                try {
-                    DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
-                    return parser.format(parser.parse(date));
-                } catch (ParseException e) {
-                    return null;
-                }
-            case TAG_MOON_PHASE:
-                return getIndexedDescription(tagType, "New", "Waxing Crescent", "First Quarter", "Waxing Gibbous", "Full", "Waning Gibbous", "Last Quarter", "Waning Crescent");
-            case TAG_AMBIENT_TEMPERATURE_FAHRENHEIT:
-            case TAG_AMBIENT_TEMPERATURE:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_SERIAL_NUMBER:
-                // default is UTF_16LE
-                StringValue svalue = _directory.getStringValue(tagType);
-                if(svalue == null)
-                    return null;
-                return svalue.toString();
-            case TAG_CONTRAST:
-            case TAG_BRIGHTNESS:
-            case TAG_SHARPNESS:
-            case TAG_SATURATION:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_INFRARED_ILLUMINATOR:
-                return getIndexedDescription(tagType, "Off", "On");
-            case TAG_USER_LABEL:
-                return _directory.getString(tagType);
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/ReconyxHyperFireMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/ReconyxHyperFireMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,105 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Reconyx HyperFire cameras.
- *
- * Reconyx uses a fixed makernote block.  Tag values are the byte index of the tag within the makernote.
- * @author Todd West http://cascadescarnivoreproject.blogspot.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ReconyxHyperFireMakernoteDirectory extends Directory
-{
-    /**
-     * Version number used for identifying makernotes from Reconyx HyperFire cameras.
-     */
-    public static final int MAKERNOTE_VERSION = 61697;
-
-    public static final int TAG_MAKERNOTE_VERSION = 0;
-    public static final int TAG_FIRMWARE_VERSION = 2;
-    public static final int TAG_TRIGGER_MODE = 12;
-    public static final int TAG_SEQUENCE = 14;
-    public static final int TAG_EVENT_NUMBER = 18;
-    public static final int TAG_DATE_TIME_ORIGINAL = 22;
-    public static final int TAG_MOON_PHASE = 36;
-    public static final int TAG_AMBIENT_TEMPERATURE_FAHRENHEIT = 38;
-    public static final int TAG_AMBIENT_TEMPERATURE = 40;
-    public static final int TAG_SERIAL_NUMBER = 42;
-    public static final int TAG_CONTRAST = 72;
-    public static final int TAG_BRIGHTNESS = 74;
-    public static final int TAG_SHARPNESS = 76;
-    public static final int TAG_SATURATION = 78;
-    public static final int TAG_INFRARED_ILLUMINATOR = 80;
-    public static final int TAG_MOTION_SENSITIVITY = 82;
-    public static final int TAG_BATTERY_VOLTAGE = 84;
-    public static final int TAG_USER_LABEL = 86;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version");
-        _tagNameMap.put(TAG_FIRMWARE_VERSION, "Firmware Version");
-        _tagNameMap.put(TAG_TRIGGER_MODE, "Trigger Mode");
-        _tagNameMap.put(TAG_SEQUENCE, "Sequence");
-        _tagNameMap.put(TAG_EVENT_NUMBER, "Event Number");
-        _tagNameMap.put(TAG_DATE_TIME_ORIGINAL, "Date/Time Original");
-        _tagNameMap.put(TAG_MOON_PHASE, "Moon Phase");
-        _tagNameMap.put(TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, "Ambient Temperature Fahrenheit");
-        _tagNameMap.put(TAG_AMBIENT_TEMPERATURE, "Ambient Temperature");
-        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_BRIGHTNESS, "Brightness");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_INFRARED_ILLUMINATOR, "Infrared Illuminator");
-        _tagNameMap.put(TAG_MOTION_SENSITIVITY, "Motion Sensitivity");
-        _tagNameMap.put(TAG_BATTERY_VOLTAGE, "Battery Voltage");
-        _tagNameMap.put(TAG_USER_LABEL, "User Label");
-    }
-
-    public ReconyxHyperFireMakernoteDirectory()
-    {
-        this.setDescriptor(new ReconyxHyperFireMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Reconyx HyperFire Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/ReconyxUltraFireMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/ReconyxUltraFireMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,110 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.StringValue;
-import com.drew.metadata.TagDescriptor;
-
-import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-
-import static com.drew.metadata.exif.makernotes.ReconyxUltraFireMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link ReconyxUltraFireMakernoteDirectory}.
- *
- * @author Todd West http://cascadescarnivoreproject.blogspot.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ReconyxUltraFireMakernoteDescriptor extends TagDescriptor<ReconyxUltraFireMakernoteDirectory>
-{
-    public ReconyxUltraFireMakernoteDescriptor(@NotNull ReconyxUltraFireMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_LABEL:
-                return _directory.getString(tagType);
-            case TAG_MAKERNOTE_ID:
-                return String.format("0x%08X", _directory.getInteger(tagType));
-            case TAG_MAKERNOTE_SIZE:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_MAKERNOTE_PUBLIC_ID:
-                return String.format("0x%08X", _directory.getInteger(tagType));
-            case TAG_MAKERNOTE_PUBLIC_SIZE:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_CAMERA_VERSION:
-            case TAG_UIB_VERSION:
-            case TAG_BTL_VERSION:
-            case TAG_PEX_VERSION:
-            case TAG_EVENT_TYPE:
-                return _directory.getString(tagType);
-            case TAG_SEQUENCE:
-                int[] sequence = _directory.getIntArray(tagType);
-                if (sequence == null)
-                    return null;
-                return String.format("%d/%d", sequence[0], sequence[1]);
-            case TAG_EVENT_NUMBER:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_DATE_TIME_ORIGINAL:
-                String date = _directory.getString(tagType);
-                try {
-                    DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
-                    return parser.format(parser.parse(date));
-                } catch (ParseException e) {
-                    return null;
-                }
-            /*case TAG_DAY_OF_WEEK:
-                return getIndexedDescription(tagType, CultureInfo.CurrentCulture.DateTimeFormat.DayNames);*/
-            case TAG_MOON_PHASE:
-                return getIndexedDescription(tagType, "New", "Waxing Crescent", "First Quarter", "Waxing Gibbous", "Full", "Waning Gibbous", "Last Quarter", "Waning Crescent");
-            case TAG_AMBIENT_TEMPERATURE_FAHRENHEIT:
-            case TAG_AMBIENT_TEMPERATURE:
-                return String.format("%d", _directory.getInteger(tagType));
-            case TAG_FLASH:
-                return getIndexedDescription(tagType, "Off", "On");
-            case TAG_BATTERY_VOLTAGE:
-                Double value = _directory.getDoubleObject(tagType);
-                DecimalFormat formatter = new DecimalFormat("0.000");
-                return value == null ? null : formatter.format(value);
-            case TAG_SERIAL_NUMBER:
-                // default is UTF_8
-                StringValue svalue = _directory.getStringValue(tagType);
-                if(svalue == null)
-                    return null;
-                return svalue.toString();
-            case TAG_USER_LABEL:
-                return _directory.getString(tagType);
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/ReconyxUltraFireMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/ReconyxUltraFireMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,116 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Reconyx UltraFire cameras.
- *
- * Reconyx uses a fixed makernote block.  Tag values are the byte index of the tag within the makernote.
- * @author Todd West http://cascadescarnivoreproject.blogspot.com
- */
-@SuppressWarnings("WeakerAccess")
-public class ReconyxUltraFireMakernoteDirectory extends Directory
-{
-    /**
-     * Version number used for identifying makernotes from Reconyx UltraFire cameras.
-     */
-    public static final int MAKERNOTE_ID = 0x00010000;
-
-    /**
-     * Version number used for identifying the public portion of makernotes from Reconyx UltraFire cameras.
-     */
-    public static final int MAKERNOTE_PUBLIC_ID = 0x07f10001;
-
-    public static final int TAG_LABEL = 0;
-    public static final int TAG_MAKERNOTE_ID = 10;
-    public static final int TAG_MAKERNOTE_SIZE = 14;
-    public static final int TAG_MAKERNOTE_PUBLIC_ID = 18;
-    public static final int TAG_MAKERNOTE_PUBLIC_SIZE = 22;
-    public static final int TAG_CAMERA_VERSION = 24;
-    public static final int TAG_UIB_VERSION = 31;
-    public static final int TAG_BTL_VERSION = 38;
-    public static final int TAG_PEX_VERSION = 45;
-    public static final int TAG_EVENT_TYPE = 52;
-    public static final int TAG_SEQUENCE = 53;
-    public static final int TAG_EVENT_NUMBER = 55;
-    public static final int TAG_DATE_TIME_ORIGINAL = 59;
-    public static final int TAG_DAY_OF_WEEK = 66;
-    public static final int TAG_MOON_PHASE = 67;
-    public static final int TAG_AMBIENT_TEMPERATURE_FAHRENHEIT = 68;
-    public static final int TAG_AMBIENT_TEMPERATURE = 70;
-    public static final int TAG_FLASH = 72;
-    public static final int TAG_BATTERY_VOLTAGE = 73;
-    public static final int TAG_SERIAL_NUMBER = 75;
-    public static final int TAG_USER_LABEL = 80;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_LABEL, "Makernote Label");
-        _tagNameMap.put(TAG_MAKERNOTE_ID, "Makernote ID");
-        _tagNameMap.put(TAG_MAKERNOTE_SIZE, "Makernote Size");
-        _tagNameMap.put(TAG_MAKERNOTE_PUBLIC_ID, "Makernote Public ID");
-        _tagNameMap.put(TAG_MAKERNOTE_PUBLIC_SIZE, "Makernote Public Size");
-        _tagNameMap.put(TAG_CAMERA_VERSION, "Camera Version");
-        _tagNameMap.put(TAG_UIB_VERSION, "Uib Version");
-        _tagNameMap.put(TAG_BTL_VERSION, "Btl Version");
-        _tagNameMap.put(TAG_PEX_VERSION, "Pex Version");
-        _tagNameMap.put(TAG_EVENT_TYPE, "Event Type");
-        _tagNameMap.put(TAG_SEQUENCE, "Sequence");
-        _tagNameMap.put(TAG_EVENT_NUMBER, "Event Number");
-        _tagNameMap.put(TAG_DATE_TIME_ORIGINAL, "Date/Time Original");
-        _tagNameMap.put(TAG_DAY_OF_WEEK, "Day of Week");
-        _tagNameMap.put(TAG_MOON_PHASE, "Moon Phase");
-        _tagNameMap.put(TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, "Ambient Temperature Fahrenheit");
-        _tagNameMap.put(TAG_AMBIENT_TEMPERATURE, "Ambient Temperature");
-        _tagNameMap.put(TAG_FLASH, "Flash");
-        _tagNameMap.put(TAG_BATTERY_VOLTAGE, "Battery Voltage");
-        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
-        _tagNameMap.put(TAG_USER_LABEL, "User Label");
-    }
-
-    public ReconyxUltraFireMakernoteDirectory()
-    {
-        this.setDescriptor(new ReconyxUltraFireMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Reconyx UltraFire Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,60 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link RicohMakernoteDirectory}.
- * <p>
- * Some information about this makernote taken from here:
- * http://www.ozhiker.com/electronics/pjmt/jpeg_info/ricoh_mn.html
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class RicohMakernoteDescriptor extends TagDescriptor<RicohMakernoteDirectory>
-{
-    public RicohMakernoteDescriptor(@NotNull RicohMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-//            case TAG_PROPRIETARY_THUMBNAIL:
-//                return getProprietaryThumbnailDataDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-//
-//    @Nullable
-//    public String getProprietaryThumbnailDataDescription()
-//    {
-//        return getByteLengthDescription(TAG_PROPRIETARY_THUMBNAIL);
-//    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/RicohMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,70 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Ricoh cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class RicohMakernoteDirectory extends Directory
-{
-    public static final int TAG_MAKERNOTE_DATA_TYPE = 0x0001;
-    public static final int TAG_VERSION = 0x0002;
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-    public static final int TAG_RICOH_CAMERA_INFO_MAKERNOTE_SUB_IFD_POINTER = 0x2001;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_MAKERNOTE_DATA_TYPE, "Makernote Data Type");
-        _tagNameMap.put(TAG_VERSION, "Version");
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-        _tagNameMap.put(TAG_RICOH_CAMERA_INFO_MAKERNOTE_SUB_IFD_POINTER, "Ricoh Camera Info Makernote Sub-IFD");
-    }
-
-    public RicohMakernoteDirectory()
-    {
-        this.setDescriptor(new RicohMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Ricoh Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SamsungType2MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SamsungType2MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,213 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.SamsungType2MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link SamsungType2MakernoteDirectory}.
- * <p>
- * Tag reference from: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Samsung.html
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SamsungType2MakernoteDescriptor extends TagDescriptor<SamsungType2MakernoteDirectory>
-{
-    public SamsungType2MakernoteDescriptor(@NotNull SamsungType2MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TagMakerNoteVersion:
-                return getMakernoteVersionDescription();
-            case TagDeviceType:
-                return getDeviceTypeDescription();
-            case TagSamsungModelId:
-                return getSamsungModelIdDescription();
-
-            case TagCameraTemperature:
-                return getCameraTemperatureDescription();
-
-            case TagFaceDetect:
-                return getFaceDetectDescription();
-            case TagFaceRecognition:
-                return getFaceRecognitionDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getMakernoteVersionDescription()
-    {
-        return getVersionBytesDescription(TagMakerNoteVersion, 2);
-    }
-
-    @Nullable
-    public String getDeviceTypeDescription()
-    {
-        Integer value = _directory.getInteger(TagDeviceType);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 0x1000:
-                return "Compact Digital Camera";
-            case 0x2000:
-                return "High-end NX Camera";
-            case 0x3000:
-                return "HXM Video Camera";
-            case 0x12000:
-                return "Cell Phone";
-            case 0x300000:
-                return "SMX Video Camera";
-            default:
-                return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getSamsungModelIdDescription()
-    {
-        Integer value = _directory.getInteger(TagSamsungModelId);
-        if (value == null)
-            return null;
-
-        switch (value)
-        {
-            case 0x100101c:
-                return "NX10";
-            /*case 0x1001226:
-                    return "HMX-S10BP";*/
-            case 0x1001226:
-                    return "HMX-S15BP";
-            case 0x1001233:
-                    return "HMX-Q10";
-            /*case 0x1001234:
-                    return "HMX-H300";*/
-            case 0x1001234:
-                    return "HMX-H304";
-            case 0x100130c:
-                    return "NX100";
-            case 0x1001327:
-                    return "NX11";
-            case 0x170104e:
-                    return "ES70, ES71 / VLUU ES70, ES71 / SL600";
-            case 0x1701052:
-                    return "ES73 / VLUU ES73 / SL605";
-            case 0x1701300:
-                    return "ES28 / VLUU ES28";
-            case 0x1701303:
-                    return "ES74,ES75,ES78 / VLUU ES75,ES78";
-            case 0x2001046:
-                    return "PL150 / VLUU PL150 / TL210 / PL151";
-            case 0x2001311:
-                    return "PL120,PL121 / VLUU PL120,PL121";
-            case 0x2001315:
-                    return "PL170,PL171 / VLUUPL170,PL171";
-            case 0x200131e:
-                    return "PL210, PL211 / VLUU PL210, PL211";
-            case 0x2701317:
-                    return "PL20,PL21 / VLUU PL20,PL21";
-            case 0x2a0001b:
-                    return "WP10 / VLUU WP10 / AQ100";
-            case 0x3000000:
-                    return "Various Models (0x3000000)";
-            case 0x3a00018:
-                    return "Various Models (0x3a00018)";
-            case 0x400101f:
-                    return "ST1000 / ST1100 / VLUU ST1000 / CL65";
-            case 0x4001022:
-                    return "ST550 / VLUU ST550 / TL225";
-            case 0x4001025:
-                    return "Various Models (0x4001025)";
-            case 0x400103e:
-                    return "VLUU ST5500, ST5500, CL80";
-            case 0x4001041:
-                    return "VLUU ST5000, ST5000, TL240";
-            case 0x4001043:
-                    return "ST70 / VLUU ST70 / ST71";
-            case 0x400130a:
-                    return "Various Models (0x400130a)";
-            case 0x400130e:
-                    return "ST90,ST91 / VLUU ST90,ST91";
-            case 0x4001313:
-                    return "VLUU ST95, ST95";
-            case 0x4a00015:
-                    return "VLUU ST60";
-            case 0x4a0135b:
-                    return "ST30, ST65 / VLUU ST65 / ST67";
-            case 0x5000000:
-                    return "Various Models (0x5000000)";
-            case 0x5001038:
-                    return "Various Models (0x5001038)";
-            case 0x500103a:
-                    return "WB650 / VLUU WB650 / WB660";
-            case 0x500103c:
-                    return "WB600 / VLUU WB600 / WB610";
-            case 0x500133e:
-                    return "WB150 / WB150F / WB152 / WB152F / WB151";
-            case 0x5a0000f:
-                    return "WB5000 / HZ25W";
-            case 0x6001036:
-                    return "EX1";
-            case 0x700131c:
-                    return "VLUU SH100, SH100";
-            case 0x27127002:
-                    return "SMX - C20N";
-            default:
-                return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    private String getCameraTemperatureDescription()
-    {
-        return getFormattedInt(TagCameraTemperature, "%d C");
-    }
-
-    @Nullable
-    public String getFaceDetectDescription()
-    {
-        return getIndexedDescription(TagFaceDetect,
-            "Off", "On");
-    }
-
-    @Nullable
-    public String getFaceRecognitionDescription()
-    {
-        return getIndexedDescription(TagFaceRecognition,
-            "Off", "On");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SamsungType2MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SamsungType2MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,89 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific certain 'newer' Samsung cameras.
- * <p>
- * Tag reference from: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Samsung.html
- *
- * @author Kevin Mott https://github.com/kwhopper
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SamsungType2MakernoteDirectory extends Directory
-{
-    // This list is incomplete
-    public static final int TagMakerNoteVersion = 0x001;
-    public static final int TagDeviceType = 0x0002;
-    public static final int TagSamsungModelId = 0x0003;
-
-    public static final int TagCameraTemperature = 0x0043;
-
-    public static final int TagFaceDetect = 0x0100;
-    public static final int TagFaceRecognition = 0x0120;
-    public static final int TagFaceName = 0x0123;
-
-    // following tags found only in SRW images
-    public static final int TagFirmwareName = 0xa001;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TagMakerNoteVersion, "Maker Note Version");
-        _tagNameMap.put(TagDeviceType, "Device Type");
-        _tagNameMap.put(TagSamsungModelId, "Model Id");
-
-        _tagNameMap.put(TagCameraTemperature, "Camera Temperature");
-
-        _tagNameMap.put(TagFaceDetect, "Face Detect");
-        _tagNameMap.put(TagFaceRecognition, "Face Recognition");
-        _tagNameMap.put(TagFaceName, "Face Name");
-        _tagNameMap.put(TagFirmwareName, "Firmware Name");
-    }
-
-    public SamsungType2MakernoteDirectory()
-    {
-        this.setDescriptor(new SamsungType2MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Samsung Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,229 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.SanyoMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link com.drew.metadata.exif.makernotes.SonyType6MakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SanyoMakernoteDescriptor extends TagDescriptor<SanyoMakernoteDirectory>
-{
-    public SanyoMakernoteDescriptor(@NotNull SanyoMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_SANYO_QUALITY:
-                return getSanyoQualityDescription();
-            case TAG_MACRO:
-                return getMacroDescription();
-            case TAG_DIGITAL_ZOOM:
-                return getDigitalZoomDescription();
-            case TAG_SEQUENTIAL_SHOT:
-                return getSequentialShotDescription();
-            case TAG_WIDE_RANGE:
-                return getWideRangeDescription();
-            case TAG_COLOR_ADJUSTMENT_MODE:
-                return getColorAdjustmentModeDescription();
-            case TAG_QUICK_SHOT:
-                return getQuickShotDescription();
-            case TAG_SELF_TIMER:
-                return getSelfTimerDescription();
-            case TAG_VOICE_MEMO:
-                return getVoiceMemoDescription();
-            case TAG_RECORD_SHUTTER_RELEASE:
-                return getRecordShutterDescription();
-            case TAG_FLICKER_REDUCE:
-                return getFlickerReduceDescription();
-            case TAG_OPTICAL_ZOOM_ON:
-                return getOptimalZoomOnDescription();
-            case TAG_DIGITAL_ZOOM_ON:
-                return getDigitalZoomOnDescription();
-            case TAG_LIGHT_SOURCE_SPECIAL:
-                return getLightSourceSpecialDescription();
-            case TAG_RESAVED:
-                return getResavedDescription();
-            case TAG_SCENE_SELECT:
-                return getSceneSelectDescription();
-            case TAG_SEQUENCE_SHOT_INTERVAL:
-                return getSequenceShotIntervalDescription();
-            case TAG_FLASH_MODE:
-                return getFlashModeDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getSanyoQualityDescription()
-    {
-        Integer value = _directory.getInteger(TAG_SANYO_QUALITY);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x0: return "Normal/Very Low";
-            case 0x1: return "Normal/Low";
-            case 0x2: return "Normal/Medium Low";
-            case 0x3: return "Normal/Medium";
-            case 0x4: return "Normal/Medium High";
-            case 0x5: return "Normal/High";
-            case 0x6: return "Normal/Very High";
-            case 0x7: return "Normal/Super High";
-            case 0x100: return "Fine/Very Low";
-            case 0x101: return "Fine/Low";
-            case 0x102: return "Fine/Medium Low";
-            case 0x103: return "Fine/Medium";
-            case 0x104: return "Fine/Medium High";
-            case 0x105: return "Fine/High";
-            case 0x106: return "Fine/Very High";
-            case 0x107: return "Fine/Super High";
-            case 0x200: return "Super Fine/Very Low";
-            case 0x201: return "Super Fine/Low";
-            case 0x202: return "Super Fine/Medium Low";
-            case 0x203: return "Super Fine/Medium";
-            case 0x204: return "Super Fine/Medium High";
-            case 0x205: return "Super Fine/High";
-            case 0x206: return "Super Fine/Very High";
-            case 0x207: return "Super Fine/Super High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    private String getMacroDescription()
-    {
-        return getIndexedDescription(TAG_MACRO, "Normal", "Macro", "View", "Manual");
-    }
-
-    @Nullable
-    private String getDigitalZoomDescription()
-    {
-        return getDecimalRational(TAG_DIGITAL_ZOOM, 3);
-    }
-
-    @Nullable
-    private String getSequentialShotDescription()
-    {
-        return getIndexedDescription(TAG_SEQUENTIAL_SHOT, "None", "Standard", "Best", "Adjust Exposure");
-    }
-
-    @Nullable
-    private String getWideRangeDescription()
-    {
-        return getIndexedDescription(TAG_WIDE_RANGE, "Off", "On");
-    }
-
-    @Nullable
-    private String getColorAdjustmentModeDescription()
-    {
-        return getIndexedDescription(TAG_COLOR_ADJUSTMENT_MODE, "Off", "On");
-    }
-
-    @Nullable
-    private String getQuickShotDescription()
-    {
-        return getIndexedDescription(TAG_QUICK_SHOT, "Off", "On");
-    }
-
-    @Nullable
-    private String getSelfTimerDescription()
-    {
-        return getIndexedDescription(TAG_SELF_TIMER, "Off", "On");
-    }
-
-    @Nullable
-    private String getVoiceMemoDescription()
-    {
-        return getIndexedDescription(TAG_VOICE_MEMO, "Off", "On");
-    }
-
-    @Nullable
-    private String getRecordShutterDescription()
-    {
-        return getIndexedDescription(TAG_RECORD_SHUTTER_RELEASE, "Record while down", "Press start, press stop");
-    }
-
-    @Nullable
-    private String getFlickerReduceDescription()
-    {
-        return getIndexedDescription(TAG_FLICKER_REDUCE, "Off", "On");
-    }
-
-    @Nullable
-    private String getOptimalZoomOnDescription()
-    {
-        return getIndexedDescription(TAG_OPTICAL_ZOOM_ON, "Off", "On");
-    }
-
-    @Nullable
-    private String getDigitalZoomOnDescription()
-    {
-        return getIndexedDescription(TAG_DIGITAL_ZOOM_ON, "Off", "On");
-    }
-
-    @Nullable
-    private String getLightSourceSpecialDescription()
-    {
-        return getIndexedDescription(TAG_LIGHT_SOURCE_SPECIAL, "Off", "On");
-    }
-
-    @Nullable
-    private String getResavedDescription()
-    {
-        return getIndexedDescription(TAG_RESAVED, "No", "Yes");
-    }
-
-    @Nullable
-    private String getSceneSelectDescription()
-    {
-        return getIndexedDescription(TAG_SCENE_SELECT,
-            "Off", "Sport", "TV", "Night", "User 1", "User 2", "Lamp");
-    }
-
-    @Nullable
-    private String getSequenceShotIntervalDescription()
-    {
-        return getIndexedDescription(TAG_SEQUENCE_SHOT_INTERVAL,
-            "5 frames/sec", "10 frames/sec", "15 frames/sec", "20 frames/sec");
-    }
-
-    @Nullable
-    private String getFlashModeDescription()
-    {
-        return getIndexedDescription(TAG_FLASH_MODE,
-            "Auto", "Force", "Disabled", "Red eye");
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SanyoMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,125 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Sanyo cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SanyoMakernoteDirectory extends Directory
-{
-    public static final int TAG_MAKERNOTE_OFFSET = 0x00ff;
-
-    public static final int TAG_SANYO_THUMBNAIL = 0x0100;
-
-    public static final int TAG_SPECIAL_MODE = 0x0200;
-    public static final int TAG_SANYO_QUALITY = 0x0201;
-    public static final int TAG_MACRO = 0x0202;
-    public static final int TAG_DIGITAL_ZOOM = 0x0204;
-    public static final int TAG_SOFTWARE_VERSION = 0x0207;
-    public static final int TAG_PICT_INFO = 0x0208;
-    public static final int TAG_CAMERA_ID = 0x0209;
-    public static final int TAG_SEQUENTIAL_SHOT = 0x020e;
-    public static final int TAG_WIDE_RANGE = 0x020f;
-    public static final int TAG_COLOR_ADJUSTMENT_MODE = 0x0210;
-    public static final int TAG_QUICK_SHOT = 0x0213;
-    public static final int TAG_SELF_TIMER = 0x0214;
-    public static final int TAG_VOICE_MEMO = 0x0216;
-    public static final int TAG_RECORD_SHUTTER_RELEASE = 0x0217;
-    public static final int TAG_FLICKER_REDUCE = 0x0218;
-    public static final int TAG_OPTICAL_ZOOM_ON = 0x0219;
-    public static final int TAG_DIGITAL_ZOOM_ON = 0x021b;
-    public static final int TAG_LIGHT_SOURCE_SPECIAL = 0x021d;
-    public static final int TAG_RESAVED = 0x021e;
-    public static final int TAG_SCENE_SELECT = 0x021f;
-    public static final int TAG_MANUAL_FOCUS_DISTANCE_OR_FACE_INFO = 0x0223;
-    public static final int TAG_SEQUENCE_SHOT_INTERVAL = 0x0224;
-    public static final int TAG_FLASH_MODE = 0x0225;
-
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    public static final int TAG_DATA_DUMP = 0x0f00;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_MAKERNOTE_OFFSET, "Makernote Offset");
-
-        _tagNameMap.put(TAG_SANYO_THUMBNAIL, "Sanyo Thumbnail");
-
-        _tagNameMap.put(TAG_SPECIAL_MODE, "Special Mode");
-        _tagNameMap.put(TAG_SANYO_QUALITY, "Sanyo Quality");
-        _tagNameMap.put(TAG_MACRO, "Macro");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM, "Digital Zoom");
-        _tagNameMap.put(TAG_SOFTWARE_VERSION, "Software Version");
-        _tagNameMap.put(TAG_PICT_INFO, "Pict Info");
-        _tagNameMap.put(TAG_CAMERA_ID, "Camera ID");
-        _tagNameMap.put(TAG_SEQUENTIAL_SHOT, "Sequential Shot");
-        _tagNameMap.put(TAG_WIDE_RANGE, "Wide Range");
-        _tagNameMap.put(TAG_COLOR_ADJUSTMENT_MODE, "Color Adjustment Node");
-        _tagNameMap.put(TAG_QUICK_SHOT, "Quick Shot");
-        _tagNameMap.put(TAG_SELF_TIMER, "Self Timer");
-        _tagNameMap.put(TAG_VOICE_MEMO, "Voice Memo");
-        _tagNameMap.put(TAG_RECORD_SHUTTER_RELEASE, "Record Shutter Release");
-        _tagNameMap.put(TAG_FLICKER_REDUCE, "Flicker Reduce");
-        _tagNameMap.put(TAG_OPTICAL_ZOOM_ON, "Optical Zoom On");
-        _tagNameMap.put(TAG_DIGITAL_ZOOM_ON, "Digital Zoom On");
-        _tagNameMap.put(TAG_LIGHT_SOURCE_SPECIAL, "Light Source Special");
-        _tagNameMap.put(TAG_RESAVED, "Resaved");
-        _tagNameMap.put(TAG_SCENE_SELECT, "Scene Select");
-        _tagNameMap.put(TAG_MANUAL_FOCUS_DISTANCE_OR_FACE_INFO, "Manual Focus Distance or Face Info");
-        _tagNameMap.put(TAG_SEQUENCE_SHOT_INTERVAL, "Sequence Shot Interval");
-        _tagNameMap.put(TAG_FLASH_MODE, "Flash Mode");
-
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print IM");
-
-        _tagNameMap.put(TAG_DATA_DUMP, "Data Dump");
-    }
-
-    public SanyoMakernoteDirectory()
-    {
-        this.setDescriptor(new SanyoMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Sanyo Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,85 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.SigmaMakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link SigmaMakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SigmaMakernoteDescriptor extends TagDescriptor<SigmaMakernoteDirectory>
-{
-    public SigmaMakernoteDescriptor(@NotNull SigmaMakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_EXPOSURE_MODE:
-                return getExposureModeDescription();
-            case TAG_METERING_MODE:
-                return getMeteringModeDescription();
-        }
-        return super.getDescription(tagType);
-    }
-
-    @Nullable
-    private String getMeteringModeDescription()
-    {
-        String value = _directory.getString(TAG_METERING_MODE);
-        if (value == null || value.length() == 0)
-            return null;
-        switch (value.charAt(0)) {
-            case '8': return "Multi Segment";
-            case 'A': return "Average";
-            case 'C': return "Center Weighted Average";
-            default:
-                return value;
-        }
-    }
-
-    @Nullable
-    private String getExposureModeDescription()
-    {
-        String value = _directory.getString(TAG_EXPOSURE_MODE);
-        if (value == null || value.length() == 0)
-            return null;
-        switch (value.charAt(0)) {
-            case 'A': return "Aperture Priority AE";
-            case 'M': return "Manual";
-            case 'P': return "Program AE";
-            case 'S': return "Shutter Speed Priority AE";
-            default:
-                return value;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SigmaMakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,110 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Sigma / Foveon cameras.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SigmaMakernoteDirectory extends Directory
-{
-    public static final int TAG_SERIAL_NUMBER = 0x2;
-    public static final int TAG_DRIVE_MODE = 0x3;
-    public static final int TAG_RESOLUTION_MODE = 0x4;
-    public static final int TAG_AUTO_FOCUS_MODE = 0x5;
-    public static final int TAG_FOCUS_SETTING = 0x6;
-    public static final int TAG_WHITE_BALANCE = 0x7;
-    public static final int TAG_EXPOSURE_MODE = 0x8;
-    public static final int TAG_METERING_MODE = 0x9;
-    public static final int TAG_LENS_RANGE = 0xa;
-    public static final int TAG_COLOR_SPACE = 0xb;
-    public static final int TAG_EXPOSURE = 0xc;
-    public static final int TAG_CONTRAST = 0xd;
-    public static final int TAG_SHADOW = 0xe;
-    public static final int TAG_HIGHLIGHT = 0xf;
-    public static final int TAG_SATURATION = 0x10;
-    public static final int TAG_SHARPNESS = 0x11;
-    public static final int TAG_FILL_LIGHT = 0x12;
-    public static final int TAG_COLOR_ADJUSTMENT = 0x14;
-    public static final int TAG_ADJUSTMENT_MODE = 0x15;
-    public static final int TAG_QUALITY = 0x16;
-    public static final int TAG_FIRMWARE = 0x17;
-    public static final int TAG_SOFTWARE = 0x18;
-    public static final int TAG_AUTO_BRACKET = 0x19;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
-        _tagNameMap.put(TAG_DRIVE_MODE, "Drive Mode");
-        _tagNameMap.put(TAG_RESOLUTION_MODE, "Resolution Mode");
-        _tagNameMap.put(TAG_AUTO_FOCUS_MODE, "Auto Focus Mode");
-        _tagNameMap.put(TAG_FOCUS_SETTING, "Focus Setting");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode");
-        _tagNameMap.put(TAG_METERING_MODE, "Metering Mode");
-        _tagNameMap.put(TAG_LENS_RANGE, "Lens Range");
-        _tagNameMap.put(TAG_COLOR_SPACE, "Color Space");
-        _tagNameMap.put(TAG_EXPOSURE, "Exposure");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_SHADOW, "Shadow");
-        _tagNameMap.put(TAG_HIGHLIGHT, "Highlight");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_FILL_LIGHT, "Fill Light");
-        _tagNameMap.put(TAG_COLOR_ADJUSTMENT, "Color Adjustment");
-        _tagNameMap.put(TAG_ADJUSTMENT_MODE, "Adjustment Mode");
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_FIRMWARE, "Firmware");
-        _tagNameMap.put(TAG_SOFTWARE, "Software");
-        _tagNameMap.put(TAG_AUTO_BRACKET, "Auto Bracket");
-    }
-
-
-    public SigmaMakernoteDirectory()
-    {
-        this.setDescriptor(new SigmaMakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Sigma Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,702 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.SonyType1MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link SonyType1MakernoteDirectory}.
- * Thanks to David Carson for the initial version of this class.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SonyType1MakernoteDescriptor extends TagDescriptor<SonyType1MakernoteDirectory>
-{
-    public SonyType1MakernoteDescriptor(@NotNull SonyType1MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_IMAGE_QUALITY:
-                return getImageQualityDescription();
-            case TAG_FLASH_EXPOSURE_COMP:
-                return getFlashExposureCompensationDescription();
-            case TAG_TELECONVERTER:
-                return getTeleconverterDescription();
-            case TAG_WHITE_BALANCE:
-                return getWhiteBalanceDescription();
-            case TAG_COLOR_TEMPERATURE:
-                return getColorTemperatureDescription();
-            case TAG_SCENE_MODE:
-                return getSceneModeDescription();
-            case TAG_ZONE_MATCHING:
-                return getZoneMatchingDescription();
-            case TAG_DYNAMIC_RANGE_OPTIMISER:
-                return getDynamicRangeOptimizerDescription();
-            case TAG_IMAGE_STABILISATION:
-                return getImageStabilizationDescription();
-            // Unfortunately it seems that there is no definite mapping between a lens ID and a lens model
-            // http://gvsoft.homedns.org/exif/makernote-sony-type1.html#0xb027
-//            case TAG_LENS_ID:
-//                return getLensIDDescription();
-            case TAG_COLOR_MODE:
-                return getColorModeDescription();
-            case TAG_MACRO:
-                return getMacroDescription();
-            case TAG_EXPOSURE_MODE:
-                return getExposureModeDescription();
-            case TAG_JPEG_QUALITY:
-                return getJpegQualityDescription();
-            case TAG_ANTI_BLUR:
-                return getAntiBlurDescription();
-            case TAG_LONG_EXPOSURE_NOISE_REDUCTION_OR_FOCUS_MODE:
-                return getLongExposureNoiseReductionDescription();
-            case TAG_HIGH_ISO_NOISE_REDUCTION:
-                return getHighIsoNoiseReductionDescription();
-            case TAG_PICTURE_EFFECT:
-                return getPictureEffectDescription();
-            case TAG_SOFT_SKIN_EFFECT:
-                return getSoftSkinEffectDescription();
-            case TAG_VIGNETTING_CORRECTION:
-                return getVignettingCorrectionDescription();
-            case TAG_LATERAL_CHROMATIC_ABERRATION:
-                return getLateralChromaticAberrationDescription();
-            case TAG_DISTORTION_CORRECTION:
-                return getDistortionCorrectionDescription();
-            case TAG_AUTO_PORTRAIT_FRAMED:
-                return getAutoPortraitFramedDescription();
-            case TAG_FOCUS_MODE:
-                return getFocusModeDescription();
-            case TAG_AF_POINT_SELECTED:
-                return getAFPointSelectedDescription();
-            case TAG_SONY_MODEL_ID:
-                return getSonyModelIdDescription();
-            case TAG_AF_MODE:
-                return getAFModeDescription();
-            case TAG_AF_ILLUMINATOR:
-                return getAFIlluminatorDescription();
-            case TAG_FLASH_LEVEL:
-                return getFlashLevelDescription();
-            case TAG_RELEASE_MODE:
-                return getReleaseModeDescription();
-            case TAG_SEQUENCE_NUMBER:
-                return getSequenceNumberDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getImageQualityDescription()
-    {
-        return getIndexedDescription(TAG_IMAGE_QUALITY,
-            "RAW",
-            "Super Fine",
-            "Fine",
-            "Standard",
-            "Economy",
-            "Extra Fine",
-            "RAW + JPEG",
-            "Compressed RAW",
-            "Compressed RAW + JPEG");
-    }
-
-    @Nullable
-    public String getFlashExposureCompensationDescription()
-    {
-        return getFormattedInt(TAG_FLASH_EXPOSURE_COMP, "%d EV");
-    }
-
-    @Nullable
-    public String getTeleconverterDescription()
-    {
-        Integer value = _directory.getInteger(TAG_TELECONVERTER);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x00: return "None";
-            case 0x48: return "Minolta/Sony AF 2x APO (D)";
-            case 0x50: return "Minolta AF 2x APO II";
-            case 0x60: return "Minolta AF 2x APO";
-            case 0x88: return "Minolta/Sony AF 1.4x APO (D)";
-            case 0x90: return "Minolta AF 1.4x APO II";
-            case 0xa0: return "Minolta AF 1.4x APO";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getWhiteBalanceDescription()
-    {
-        Integer value = _directory.getInteger(TAG_WHITE_BALANCE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0x00: return "Auto";
-            case 0x01: return "Color Temperature/Color Filter";
-            case 0x10: return "Daylight";
-            case 0x20: return "Cloudy";
-            case 0x30: return "Shade";
-            case 0x40: return "Tungsten";
-            case 0x50: return "Flash";
-            case 0x60: return "Fluorescent";
-            case 0x70: return "Custom";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getColorTemperatureDescription()
-    {
-        Integer value = _directory.getInteger(TAG_COLOR_TEMPERATURE);
-        if (value == null)
-            return null;
-        if (value == 0)
-            return "Auto";
-        int kelvin = ((value & 0x00FF0000) >> 8) | ((value & 0xFF000000) >> 24);
-        return String.format("%d K", kelvin);
-    }
-
-    @Nullable
-    public String getZoneMatchingDescription()
-    {
-        return getIndexedDescription(TAG_ZONE_MATCHING,
-            "ISO Setting Used", "High Key", "Low Key");
-    }
-
-    @Nullable
-    public String getDynamicRangeOptimizerDescription()
-    {
-        Integer value = _directory.getInteger(TAG_DYNAMIC_RANGE_OPTIMISER);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "Standard";
-            case 2: return "Advanced Auto";
-            case 3: return "Auto";
-            case 8: return "Advanced LV1";
-            case 9: return "Advanced LV2";
-            case 10: return "Advanced LV3";
-            case 11: return "Advanced LV4";
-            case 12: return "Advanced LV5";
-            case 16: return "LV1";
-            case 17: return "LV2";
-            case 18: return "LV3";
-            case 19: return "LV4";
-            case 20: return "LV5";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getImageStabilizationDescription()
-    {
-        Integer value = _directory.getInteger(TAG_IMAGE_STABILISATION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "On";
-            default: return "N/A";
-        }
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_COLOR_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Standard";
-            case 1: return "Vivid";
-            case 2: return "Portrait";
-            case 3: return "Landscape";
-            case 4: return "Sunset";
-            case 5: return "Night Portrait";
-            case 6: return "Black & White";
-            case 7: return "Adobe RGB";
-            case 12: case 100: return "Neutral";
-            case 13: case 101: return "Clear";
-            case 14: case 102: return "Deep";
-            case 15: case 103: return "Light";
-            case 16: return "Autumn";
-            case 17: return "Sepia";
-            case 104: return "Night View";
-            case 105: return "Autumn Leaves";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getMacroDescription()
-    {
-        Integer value = _directory.getInteger(TAG_MACRO);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "On";
-            case 2: return "Magnifying Glass/Super Macro";
-            case 0xFFFF: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getExposureModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_EXPOSURE_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Program";
-            case 1: return "Portrait";
-            case 2: return "Beach";
-            case 3: return "Sports";
-            case 4: return "Snow";
-            case 5: return "Landscape";
-            case 6: return "Auto";
-            case 7: return "Aperture Priority";
-            case 8: return "Shutter Priority";
-            case 9: return "Night Scene / Twilight";
-            case 10: return "Hi-Speed Shutter";
-            case 11: return "Twilight Portrait";
-            case 12: return "Soft Snap/Portrait";
-            case 13: return "Fireworks";
-            case 14: return "Smile Shutter";
-            case 15: return "Manual";
-            case 18: return "High Sensitivity";
-            case 19: return "Macro";
-            case 20: return "Advanced Sports Shooting";
-            case 29: return "Underwater";
-            case 33: return "Food";
-            case 34: return "Panorama";
-            case 35: return "Handheld Night Shot";
-            case 36: return "Anti Motion Blur";
-            case 37: return "Pet";
-            case 38: return "Backlight Correction HDR";
-            case 39: return "Superior Auto";
-            case 40: return "Background Defocus";
-            case 41: return "Soft Skin";
-            case 42: return "3D Image";
-            case 0xFFFF: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getJpegQualityDescription()
-    {
-        Integer value = _directory.getInteger(TAG_JPEG_QUALITY);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Normal";
-            case 1: return "Fine";
-            case 2: return "Extra Fine";
-            case 0xFFFF: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getAntiBlurDescription()
-    {
-        Integer value = _directory.getInteger(TAG_ANTI_BLUR);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "On (Continuous)";
-            case 2: return "On (Shooting)";
-            case 0xFFFF: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getLongExposureNoiseReductionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_LONG_EXPOSURE_NOISE_REDUCTION_OR_FOCUS_MODE);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "On";
-            case 0xFFFF: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getHighIsoNoiseReductionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_HIGH_ISO_NOISE_REDUCTION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "On";
-            case 2: return "Normal";
-            case 3: return "High";
-            case 0x100: return "Auto";
-            case 0xffff: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getPictureEffectDescription()
-    {
-        Integer value = _directory.getInteger(TAG_PICTURE_EFFECT);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "Toy Camera";
-            case 2: return "Pop Color";
-            case 3: return "Posterization";
-            case 4: return "Posterization B/W";
-            case 5: return "Retro Photo";
-            case 6: return "Soft High Key";
-            case 7: return "Partial Color (red)";
-            case 8: return "Partial Color (green)";
-            case 9: return "Partial Color (blue)";
-            case 10: return "Partial Color (yellow)";
-            case 13: return "High Contrast Monochrome";
-            case 16: return "Toy Camera (normal)";
-            case 17: return "Toy Camera (cool)";
-            case 18: return "Toy Camera (warm)";
-            case 19: return "Toy Camera (green)";
-            case 20: return "Toy Camera (magenta)";
-            case 32: return "Soft Focus (low)";
-            case 33: return "Soft Focus";
-            case 34: return "Soft Focus (high)";
-            case 48: return "Miniature (auto)";
-            case 49: return "Miniature (top)";
-            case 50: return "Miniature (middle horizontal)";
-            case 51: return "Miniature (bottom)";
-            case 52: return "Miniature (left)";
-            case 53: return "Miniature (middle vertical)";
-            case 54: return "Miniature (right)";
-            case 64: return "HDR Painting (low)";
-            case 65: return "HDR Painting";
-            case 66: return "HDR Painting (high)";
-            case 80: return "Rich-tone Monochrome";
-            case 97: return "Water Color";
-            case 98: return "Water Color 2";
-            case 112: return "Illustration (low)";
-            case 113: return "Illustration";
-            case 114: return "Illustration (high)";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getSoftSkinEffectDescription()
-    {
-        return getIndexedDescription(TAG_SOFT_SKIN_EFFECT, "Off", "Low", "Mid", "High");
-    }
-
-    @Nullable
-    public String getVignettingCorrectionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_VIGNETTING_CORRECTION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 2: return "Auto";
-            case 0xffffffff: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getLateralChromaticAberrationDescription()
-    {
-        Integer value = _directory.getInteger(TAG_LATERAL_CHROMATIC_ABERRATION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 2: return "Auto";
-            case 0xffffffff: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getDistortionCorrectionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_DISTORTION_CORRECTION);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "Off";
-            case 2: return "Auto";
-            case 0xffffffff: return "N/A";
-            default: return String.format("Unknown (%d)", value);
-        }
-    }
-
-    @Nullable
-    public String getAutoPortraitFramedDescription()
-    {
-        return getIndexedDescription(TAG_AUTO_PORTRAIT_FRAMED, "No", "Yes");
-    }
-
-    @Nullable
-    public String getFocusModeDescription()
-    {
-        return getIndexedDescription(TAG_FOCUS_MODE,
-            "Manual", null, "AF-A", "AF-C", "AF-S", null, "DMF", "AF-D");
-    }
-
-    @Nullable
-    public String getAFPointSelectedDescription()
-    {
-        return getIndexedDescription(TAG_AF_POINT_SELECTED,
-            "Auto", // 0
-            "Center", // 1
-            "Top", // 2
-            "Upper-right", // 3
-            "Right", // 4
-            "Lower-right", // 5
-            "Bottom", // 6
-            "Lower-left", // 7
-            "Left", // 8
-            "Upper-left	  	", // 9
-            "Far Right", // 10
-            "Far Left", // 11
-            "Upper-middle", // 12
-            "Near Right", // 13
-            "Lower-middle", // 14
-            "Near Left", // 15
-            "Upper Far Right", // 16
-            "Lower Far Right", // 17
-            "Lower Far Left", // 18
-            "Upper Far Left" // 19
-        );
-    }
-
-    @Nullable
-    public String getSonyModelIdDescription()
-    {
-        Integer value = _directory.getInteger(TAG_SONY_MODEL_ID);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 2: return "DSC-R1";
-            case 256: return "DSLR-A100";
-            case 257: return "DSLR-A900";
-            case 258: return "DSLR-A700";
-            case 259: return "DSLR-A200";
-            case 260: return "DSLR-A350";
-            case 261: return "DSLR-A300";
-            case 262: return "DSLR-A900 (APS-C mode)";
-            case 263: return "DSLR-A380/A390";
-            case 264: return "DSLR-A330";
-            case 265: return "DSLR-A230";
-            case 266: return "DSLR-A290";
-            case 269: return "DSLR-A850";
-            case 270: return "DSLR-A850 (APS-C mode)";
-            case 273: return "DSLR-A550";
-            case 274: return "DSLR-A500";
-            case 275: return "DSLR-A450";
-            case 278: return "NEX-5";
-            case 279: return "NEX-3";
-            case 280: return "SLT-A33";
-            case 281: return "SLT-A55V";
-            case 282: return "DSLR-A560";
-            case 283: return "DSLR-A580";
-            case 284: return "NEX-C3";
-            case 285: return "SLT-A35";
-            case 286: return "SLT-A65V";
-            case 287: return "SLT-A77V";
-            case 288: return "NEX-5N";
-            case 289: return "NEX-7";
-            case 290: return "NEX-VG20E";
-            case 291: return "SLT-A37";
-            case 292: return "SLT-A57";
-            case 293: return "NEX-F3";
-            case 294: return "SLT-A99V";
-            case 295: return "NEX-6";
-            case 296: return "NEX-5R";
-            case 297: return "DSC-RX100";
-            case 298: return "DSC-RX1";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSceneModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_SCENE_MODE);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0: return "Standard";
-            case 1: return "Portrait";
-            case 2: return "Text";
-            case 3: return "Night Scene";
-            case 4: return "Sunset";
-            case 5: return "Sports";
-            case 6: return "Landscape";
-            case 7: return "Night Portrait";
-            case 8: return "Macro";
-            case 9: return "Super Macro";
-            case 16: return "Auto";
-            case 17: return "Night View/Portrait";
-            case 18: return "Sweep Panorama";
-            case 19: return "Handheld Night Shot";
-            case 20: return "Anti Motion Blur";
-            case 21: return "Cont. Priority AE";
-            case 22: return "Auto+";
-            case 23: return "3D Sweep Panorama";
-            case 24: return "Superior Auto";
-            case 25: return "High Sensitivity";
-            case 26: return "Fireworks";
-            case 27: return "Food";
-            case 28: return "Pet";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAFModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_AF_MODE);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0: return "Default";
-            case 1: return "Multi";
-            case 2: return "Center";
-            case 3: return "Spot";
-            case 4: return "Flexible Spot";
-            case 6: return "Touch";
-            case 14: return "Manual Focus";
-            case 15: return "Face Detected";
-            case 0xffff: return "n/a";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getAFIlluminatorDescription()
-    {
-        Integer value = _directory.getInteger(TAG_AF_ILLUMINATOR);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0: return "Off";
-            case 1: return "Auto";
-            case 0xffff: return "n/a";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getFlashLevelDescription()
-    {
-        Integer value = _directory.getInteger(TAG_FLASH_LEVEL);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case -32768: return "Low";
-            case -3: return "-3/3";
-            case -2: return "-2/3";
-            case -1: return "-1/3";
-            case 0: return "Normal";
-            case 1: return "+1/3";
-            case 2: return "+2/3";
-            case 3: return "+3/3";
-            case 128: return "n/a";
-            case 32767: return "High";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getReleaseModeDescription()
-    {
-        Integer value = _directory.getInteger(TAG_RELEASE_MODE);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0: return "Normal";
-            case 2: return "Continuous";
-            case 5: return "Exposure Bracketing";
-            case 6: return "White Balance Bracketing";
-            case 65535: return "n/a";
-            default:
-                return "Unknown (" + value + ")";
-        }
-    }
-
-    @Nullable
-    public String getSequenceNumberDescription()
-    {
-        Integer value = _directory.getInteger(TAG_RELEASE_MODE);
-
-        if (value == null)
-            return null;
-
-        switch (value) {
-            case 0: return "Single";
-            case 65535: return "n/a";
-            default:
-                return value.toString();
-        }
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SonyType1MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,229 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Sony cameras that use the Sony Type 1 makernote tags.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SonyType1MakernoteDirectory extends Directory
-{
-    public static final int TAG_CAMERA_INFO = 0x0010;
-    public static final int TAG_FOCUS_INFO = 0x0020;
-
-    public static final int TAG_IMAGE_QUALITY = 0x0102;
-    public static final int TAG_FLASH_EXPOSURE_COMP = 0x0104;
-    public static final int TAG_TELECONVERTER = 0x0105;
-
-    public static final int TAG_WHITE_BALANCE_FINE_TUNE = 0x0112;
-    public static final int TAG_CAMERA_SETTINGS = 0x0114;
-    public static final int TAG_WHITE_BALANCE = 0x0115;
-    public static final int TAG_EXTRA_INFO = 0x0116;
-
-    public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00;
-
-    public static final int TAG_MULTI_BURST_MODE = 0x1000;
-    public static final int TAG_MULTI_BURST_IMAGE_WIDTH = 0x1001;
-    public static final int TAG_MULTI_BURST_IMAGE_HEIGHT = 0x1002;
-    public static final int TAG_PANORAMA = 0x1003;
-
-    public static final int TAG_PREVIEW_IMAGE = 0x2001;
-    public static final int TAG_RATING = 0x2002;
-    public static final int TAG_CONTRAST = 0x2004;
-    public static final int TAG_SATURATION = 0x2005;
-    public static final int TAG_SHARPNESS = 0x2006;
-    public static final int TAG_BRIGHTNESS = 0x2007;
-    public static final int TAG_LONG_EXPOSURE_NOISE_REDUCTION = 0x2008;
-    public static final int TAG_HIGH_ISO_NOISE_REDUCTION = 0x2009;
-    public static final int TAG_HDR = 0x200a;
-    public static final int TAG_MULTI_FRAME_NOISE_REDUCTION = 0x200b;
-    public static final int TAG_PICTURE_EFFECT = 0x200e;
-    public static final int TAG_SOFT_SKIN_EFFECT = 0x200f;
-
-    public static final int TAG_VIGNETTING_CORRECTION = 0x2011;
-    public static final int TAG_LATERAL_CHROMATIC_ABERRATION = 0x2012;
-    public static final int TAG_DISTORTION_CORRECTION = 0x2013;
-    public static final int TAG_WB_SHIFT_AMBER_MAGENTA = 0x2014;
-    public static final int TAG_AUTO_PORTRAIT_FRAMED = 0x2016;
-    public static final int TAG_FOCUS_MODE = 0x201b;
-    public static final int TAG_AF_POINT_SELECTED = 0x201e;
-
-    public static final int TAG_SHOT_INFO = 0x3000;
-
-    public static final int TAG_FILE_FORMAT = 0xb000;
-    public static final int TAG_SONY_MODEL_ID = 0xb001;
-
-    public static final int TAG_COLOR_MODE_SETTING = 0xb020;
-    public static final int TAG_COLOR_TEMPERATURE = 0xb021;
-    public static final int TAG_COLOR_COMPENSATION_FILTER = 0xb022;
-    public static final int TAG_SCENE_MODE = 0xb023;
-    public static final int TAG_ZONE_MATCHING = 0xb024;
-    public static final int TAG_DYNAMIC_RANGE_OPTIMISER = 0xb025;
-    public static final int TAG_IMAGE_STABILISATION = 0xb026;
-    public static final int TAG_LENS_ID = 0xb027;
-    public static final int TAG_MINOLTA_MAKERNOTE = 0xb028;
-    public static final int TAG_COLOR_MODE = 0xb029;
-    public static final int TAG_LENS_SPEC = 0xb02a;
-    public static final int TAG_FULL_IMAGE_SIZE = 0xb02b;
-    public static final int TAG_PREVIEW_IMAGE_SIZE = 0xb02c;
-
-    public static final int TAG_MACRO = 0xb040;
-    public static final int TAG_EXPOSURE_MODE = 0xb041;
-    public static final int TAG_FOCUS_MODE_2 = 0xb042;
-    public static final int TAG_AF_MODE = 0xb043;
-    public static final int TAG_AF_ILLUMINATOR = 0xb044;
-    public static final int TAG_JPEG_QUALITY = 0xb047;
-    public static final int TAG_FLASH_LEVEL = 0xb048;
-    public static final int TAG_RELEASE_MODE = 0xb049;
-    public static final int TAG_SEQUENCE_NUMBER = 0xb04a;
-    public static final int TAG_ANTI_BLUR = 0xb04b;
-    /**
-     * (FocusMode for RX100)
-     * 0 = Manual
-     * 2 = AF-S
-     * 3 = AF-C
-     * 5 = Semi-manual
-     * 6 = Direct Manual Focus
-     * (LongExposureNoiseReduction for other models)
-     * 0 = Off
-     * 1 = On
-     * 2 = On 2
-     * 65535 = n/a
-     */
-    public static final int TAG_LONG_EXPOSURE_NOISE_REDUCTION_OR_FOCUS_MODE = 0xb04e;
-    public static final int TAG_DYNAMIC_RANGE_OPTIMIZER = 0xb04f;
-
-    public static final int TAG_HIGH_ISO_NOISE_REDUCTION_2 = 0xb050;
-    public static final int TAG_INTELLIGENT_AUTO = 0xb052;
-    public static final int TAG_WHITE_BALANCE_2 = 0xb054;
-
-    public static final int TAG_NO_PRINT = 0xFFFF;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_CAMERA_INFO, "Camera Info");
-        _tagNameMap.put(TAG_FOCUS_INFO, "Focus Info");
-
-        _tagNameMap.put(TAG_IMAGE_QUALITY, "Image Quality");
-        _tagNameMap.put(TAG_FLASH_EXPOSURE_COMP, "Flash Exposure Compensation");
-        _tagNameMap.put(TAG_TELECONVERTER, "Teleconverter Model");
-
-        _tagNameMap.put(TAG_WHITE_BALANCE_FINE_TUNE, "White Balance Fine Tune Value");
-        _tagNameMap.put(TAG_CAMERA_SETTINGS, "Camera Settings");
-        _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance");
-        _tagNameMap.put(TAG_EXTRA_INFO, "Extra Info");
-
-        _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info");
-
-        _tagNameMap.put(TAG_MULTI_BURST_MODE, "Multi Burst Mode");
-        _tagNameMap.put(TAG_MULTI_BURST_IMAGE_WIDTH, "Multi Burst Image Width");
-        _tagNameMap.put(TAG_MULTI_BURST_IMAGE_HEIGHT, "Multi Burst Image Height");
-        _tagNameMap.put(TAG_PANORAMA, "Panorama");
-
-        _tagNameMap.put(TAG_PREVIEW_IMAGE, "Preview Image");
-        _tagNameMap.put(TAG_RATING, "Rating");
-        _tagNameMap.put(TAG_CONTRAST, "Contrast");
-        _tagNameMap.put(TAG_SATURATION, "Saturation");
-        _tagNameMap.put(TAG_SHARPNESS, "Sharpness");
-        _tagNameMap.put(TAG_BRIGHTNESS, "Brightness");
-        _tagNameMap.put(TAG_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction");
-        _tagNameMap.put(TAG_HIGH_ISO_NOISE_REDUCTION, "High ISO Noise Reduction");
-        _tagNameMap.put(TAG_HDR, "HDR");
-        _tagNameMap.put(TAG_MULTI_FRAME_NOISE_REDUCTION, "Multi Frame Noise Reduction");
-        _tagNameMap.put(TAG_PICTURE_EFFECT, "Picture Effect");
-        _tagNameMap.put(TAG_SOFT_SKIN_EFFECT, "Soft Skin Effect");
-
-        _tagNameMap.put(TAG_VIGNETTING_CORRECTION, "Vignetting Correction");
-        _tagNameMap.put(TAG_LATERAL_CHROMATIC_ABERRATION, "Lateral Chromatic Aberration");
-        _tagNameMap.put(TAG_DISTORTION_CORRECTION, "Distortion Correction");
-        _tagNameMap.put(TAG_WB_SHIFT_AMBER_MAGENTA, "WB Shift Amber/Magenta");
-        _tagNameMap.put(TAG_AUTO_PORTRAIT_FRAMED, "Auto Portrait Framing");
-        _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode");
-        _tagNameMap.put(TAG_AF_POINT_SELECTED, "AF Point Selected");
-
-        _tagNameMap.put(TAG_SHOT_INFO, "Shot Info");
-
-        _tagNameMap.put(TAG_FILE_FORMAT, "File Format");
-        _tagNameMap.put(TAG_SONY_MODEL_ID, "Sony Model ID");
-
-        _tagNameMap.put(TAG_COLOR_MODE_SETTING, "Color Mode Setting");
-        _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature");
-        _tagNameMap.put(TAG_COLOR_COMPENSATION_FILTER, "Color Compensation Filter");
-        _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode");
-        _tagNameMap.put(TAG_ZONE_MATCHING, "Zone Matching");
-        _tagNameMap.put(TAG_DYNAMIC_RANGE_OPTIMISER, "Dynamic Range Optimizer");
-        _tagNameMap.put(TAG_IMAGE_STABILISATION, "Image Stabilisation");
-        _tagNameMap.put(TAG_LENS_ID, "Lens ID");
-        _tagNameMap.put(TAG_MINOLTA_MAKERNOTE, "Minolta Makernote");
-        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
-        _tagNameMap.put(TAG_LENS_SPEC, "Lens Spec");
-        _tagNameMap.put(TAG_FULL_IMAGE_SIZE, "Full Image Size");
-        _tagNameMap.put(TAG_PREVIEW_IMAGE_SIZE, "Preview Image Size");
-
-        _tagNameMap.put(TAG_MACRO, "Macro");
-        _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode");
-        _tagNameMap.put(TAG_FOCUS_MODE_2, "Focus Mode");
-        _tagNameMap.put(TAG_AF_MODE, "AF Mode");
-        _tagNameMap.put(TAG_AF_ILLUMINATOR, "AF Illuminator");
-        _tagNameMap.put(TAG_JPEG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_FLASH_LEVEL, "Flash Level");
-        _tagNameMap.put(TAG_RELEASE_MODE, "Release Mode");
-        _tagNameMap.put(TAG_SEQUENCE_NUMBER, "Sequence Number");
-        _tagNameMap.put(TAG_ANTI_BLUR, "Anti Blur");
-        _tagNameMap.put(TAG_LONG_EXPOSURE_NOISE_REDUCTION_OR_FOCUS_MODE, "Long Exposure Noise Reduction");
-        _tagNameMap.put(TAG_DYNAMIC_RANGE_OPTIMIZER, "Dynamic Range Optimizer");
-
-        _tagNameMap.put(TAG_HIGH_ISO_NOISE_REDUCTION_2, "High ISO Noise Reduction");
-        _tagNameMap.put(TAG_INTELLIGENT_AUTO, "Intelligent Auto");
-        _tagNameMap.put(TAG_WHITE_BALANCE_2, "White Balance 2");
-
-        _tagNameMap.put(TAG_NO_PRINT, "No Print");
-    }
-
-    public SonyType1MakernoteDirectory()
-    {
-        this.setDescriptor(new SonyType1MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Sony Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,60 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.exif.makernotes.SonyType6MakernoteDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link SonyType6MakernoteDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SonyType6MakernoteDescriptor extends TagDescriptor<SonyType6MakernoteDirectory>
-{
-    public SonyType6MakernoteDescriptor(@NotNull SonyType6MakernoteDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_MAKERNOTE_THUMB_VERSION:
-                return getMakernoteThumbVersionDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getMakernoteThumbVersionDescription()
-    {
-        return getVersionBytesDescription(TAG_MAKERNOTE_THUMB_VERSION, 2);
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/SonyType6MakernoteDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,71 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.exif.makernotes;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags specific to Sony cameras that use the Sony Type 6 makernote tags.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class SonyType6MakernoteDirectory extends Directory
-{
-    public static final int TAG_MAKERNOTE_THUMB_OFFSET = 0x0513;
-    public static final int TAG_MAKERNOTE_THUMB_LENGTH = 0x0514;
-//    public static final int TAG_UNKNOWN_1 = 0x0515;
-    public static final int TAG_MAKERNOTE_THUMB_VERSION = 0x2000;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_MAKERNOTE_THUMB_OFFSET, "Makernote Thumb Offset");
-        _tagNameMap.put(TAG_MAKERNOTE_THUMB_LENGTH, "Makernote Thumb Length");
-//        _tagNameMap.put(TAG_UNKNOWN_1, "Sony-6-0x0203");
-        _tagNameMap.put(TAG_MAKERNOTE_THUMB_VERSION, "Makernote Thumb Version");
-    }
-
-    public SonyType6MakernoteDirectory()
-    {
-        this.setDescriptor(new SonyType6MakernoteDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Sony Makernote";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/exif/makernotes/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/makernotes/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,5 +1,0 @@
-/**
- * Contains {@link com.drew.metadata.Directory} and {@link com.drew.metadata.TagDescriptor} classes related to the
- * modelling of manufacturer-specific makernotes.
- */
-package com.drew.metadata.exif.makernotes;
Index: unk/src/com/drew/metadata/exif/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/exif/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of Exif metadata and camera manufacturer-specific makernotes.
- */
-package com.drew.metadata.exif;
Index: unk/src/com/drew/metadata/file/FileSystemDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/file/FileSystemDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,63 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.file;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.file.FileSystemDirectory.*;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class FileSystemDescriptor extends TagDescriptor<FileSystemDirectory>
-{
-    public FileSystemDescriptor(@NotNull FileSystemDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_FILE_SIZE:
-                return getFileSizeDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    private String getFileSizeDescription()
-    {
-        Long size = _directory.getLongObject(TAG_FILE_SIZE);
-
-        if (size == null)
-            return null;
-
-        return Long.toString(size) + " bytes";
-    }
-}
-
Index: unk/src/com/drew/metadata/file/FileSystemDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/file/FileSystemDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,65 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.file;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class FileSystemDirectory extends Directory
-{
-    public static final int TAG_FILE_NAME = 1;
-    public static final int TAG_FILE_SIZE = 2;
-    public static final int TAG_FILE_MODIFIED_DATE = 3;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_FILE_NAME, "File Name");
-        _tagNameMap.put(TAG_FILE_SIZE, "File Size");
-        _tagNameMap.put(TAG_FILE_MODIFIED_DATE, "File Modified Date");
-    }
-
-    public FileSystemDirectory()
-    {
-        this.setDescriptor(new FileSystemDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "File";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/file/FileSystemMetadataReader.java
===================================================================
--- /trunk/src/com/drew/metadata/file/FileSystemMetadataReader.java	(revision 16024)
+++ 	(revision )
@@ -1,51 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.file;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-
-import java.io.*;
-import java.util.Date;
-
-public class FileSystemMetadataReader
-{
-    public void read(@NotNull File file, @NotNull Metadata metadata) throws IOException
-    {
-        if (!file.isFile())
-            throw new IOException("File object must reference a file");
-        if (!file.exists())
-            throw new IOException("File does not exist");
-        if (!file.canRead())
-            throw new IOException("File is not readable");
-
-        FileSystemDirectory directory = metadata.getFirstDirectoryOfType(FileSystemDirectory.class);
-
-        if (directory == null) {
-            directory = new FileSystemDirectory();
-            metadata.addDirectory(directory);
-        }
-
-        directory.setString(FileSystemDirectory.TAG_FILE_NAME, file.getName());
-        directory.setLong(FileSystemDirectory.TAG_FILE_SIZE, file.length());
-        directory.setDate(FileSystemDirectory.TAG_FILE_MODIFIED_DATE, new Date(file.lastModified()));
-    }
-}
Index: unk/src/com/drew/metadata/file/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/file/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,6 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of file system metadata.
- *
- * @since 2.8.0
- */
-package com.drew.metadata.file;
Index: unk/src/com/drew/metadata/icc/IccDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/icc/IccDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,342 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.icc;
-
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.text.DecimalFormat;
-
-import static com.drew.metadata.icc.IccDirectory.*;
-
-/**
- * @author Yuri Binev
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class IccDescriptor extends TagDescriptor<IccDirectory>
-{
-    public IccDescriptor(@NotNull IccDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_PROFILE_VERSION:
-                return getProfileVersionDescription();
-            case TAG_PROFILE_CLASS:
-                return getProfileClassDescription();
-            case TAG_PLATFORM:
-                return getPlatformDescription();
-            case TAG_RENDERING_INTENT:
-                return getRenderingIntentDescription();
-        }
-
-        if (tagType > 0x20202020 && tagType < 0x7a7a7a7a)
-            return getTagDataString(tagType);
-
-        return super.getDescription(tagType);
-    }
-
-    private static final int ICC_TAG_TYPE_TEXT = 0x74657874;
-    private static final int ICC_TAG_TYPE_DESC = 0x64657363;
-    private static final int ICC_TAG_TYPE_SIG = 0x73696720;
-    private static final int ICC_TAG_TYPE_MEAS = 0x6D656173;
-    private static final int ICC_TAG_TYPE_XYZ_ARRAY = 0x58595A20;
-    private static final int ICC_TAG_TYPE_MLUC = 0x6d6c7563;
-    private static final int ICC_TAG_TYPE_CURV = 0x63757276;
-
-    @Nullable
-    private String getTagDataString(int tagType)
-    {
-        try {
-            byte[] bytes = _directory.getByteArray(tagType);
-            if (bytes == null)
-                return _directory.getString(tagType);
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            int iccTagType = reader.getInt32(0);
-            switch (iccTagType) {
-                case ICC_TAG_TYPE_TEXT:
-                    try {
-                        return new String(bytes, 8, bytes.length - 8 - 1, "ASCII");
-                    } catch (UnsupportedEncodingException ex) {
-                        return new String(bytes, 8, bytes.length - 8 - 1);
-                    }
-                case ICC_TAG_TYPE_DESC:
-                    int stringLength = reader.getInt32(8);
-                    return new String(bytes, 12, stringLength - 1);
-                case ICC_TAG_TYPE_SIG:
-                    return IccReader.getStringFromInt32(reader.getInt32(8));
-                case ICC_TAG_TYPE_MEAS: {
-                    int observerType = reader.getInt32(8);
-                    float x = reader.getS15Fixed16(12);
-                    float y = reader.getS15Fixed16(16);
-                    float z = reader.getS15Fixed16(20);
-                    int geometryType = reader.getInt32(24);
-                    float flare = reader.getS15Fixed16(28);
-                    int illuminantType = reader.getInt32(32);
-                    String observerString;
-                    switch (observerType) {
-                        case 0:
-                            observerString = "Unknown";
-                            break;
-                        case 1:
-                            observerString = "1931 2\u00B0";
-                            break;
-                        case 2:
-                            observerString = "1964 10\u00B0";
-                            break;
-                        default:
-                            observerString = String.format("Unknown %d", observerType);
-                    }
-                    String geometryString;
-                    switch (geometryType) {
-                        case 0:
-                            geometryString = "Unknown";
-                            break;
-                        case 1:
-                            geometryString = "0/45 or 45/0";
-                            break;
-                        case 2:
-                            geometryString = "0/d or d/0";
-                            break;
-                        default:
-                            geometryString = String.format("Unknown %d", observerType);
-                    }
-                    String illuminantString;
-                    switch (illuminantType) {
-                        case 0:
-                            illuminantString = "unknown";
-                            break;
-                        case 1:
-                            illuminantString = "D50";
-                            break;
-                        case 2:
-                            illuminantString = "D65";
-                            break;
-                        case 3:
-                            illuminantString = "D93";
-                            break;
-                        case 4:
-                            illuminantString = "F2";
-                            break;
-                        case 5:
-                            illuminantString = "D55";
-                            break;
-                        case 6:
-                            illuminantString = "A";
-                            break;
-                        case 7:
-                            illuminantString = "Equi-Power (E)";
-                            break;
-                        case 8:
-                            illuminantString = "F8";
-                            break;
-                        default:
-                            illuminantString = String.format("Unknown %d", illuminantType);
-                            break;
-                    }
-                    DecimalFormat format = new DecimalFormat("0.###");
-                    return String.format("%s Observer, Backing (%s, %s, %s), Geometry %s, Flare %d%%, Illuminant %s",
-                            observerString, format.format(x), format.format(y), format.format(z), geometryString, Math.round(flare * 100), illuminantString);
-                }
-                case ICC_TAG_TYPE_XYZ_ARRAY: {
-                    StringBuilder res = new StringBuilder();
-                    DecimalFormat format = new DecimalFormat("0.####");
-                    int count = (bytes.length - 8) / 12;
-                    for (int i = 0; i < count; i++) {
-                        float x = reader.getS15Fixed16(8 + i * 12);
-                        float y = reader.getS15Fixed16(8 + i * 12 + 4);
-                        float z = reader.getS15Fixed16(8 + i * 12 + 8);
-                        if (i > 0)
-                            res.append(", ");
-                        res.append("(").append(format.format(x)).append(", ").append(format.format(y)).append(", ").append(format.format(z)).append(")");
-                    }
-                    return res.toString();
-                }
-                case ICC_TAG_TYPE_MLUC: {
-                    int int1 = reader.getInt32(8);
-                    StringBuilder res = new StringBuilder();
-                    res.append(int1);
-                    //int int2 = reader.getInt32(12);
-                    //System.err.format("int1: %d, int2: %d\n", int1, int2);
-                    for (int i = 0; i < int1; i++) {
-                        String str = IccReader.getStringFromInt32(reader.getInt32(16 + i * 12));
-                        int len = reader.getInt32(16 + i * 12 + 4);
-                        int ofs = reader.getInt32(16 + i * 12 + 8);
-                        String name;
-                        try {
-                            name = new String(bytes, ofs, len, "UTF-16BE");
-                        } catch (UnsupportedEncodingException ex) {
-                            name = new String(bytes, ofs, len);
-                        }
-                        res.append(" ").append(str).append("(").append(name).append(")");
-                        //System.err.format("% 3d: %s, len: %d, ofs: %d, \"%s\"\n", i, str, len,ofs,name);
-                    }
-                    return res.toString();
-                }
-                case ICC_TAG_TYPE_CURV: {
-                    int num = reader.getInt32(8);
-                    StringBuilder res = new StringBuilder();
-                    for (int i = 0; i < num; i++) {
-                        if (i != 0)
-                            res.append(", ");
-                        res.append(formatDoubleAsString(((float)reader.getUInt16(12 + i * 2)) / 65535.0, 7, false));
-                        //res+=String.format("%1.7g",Math.round(((float)iccReader.getInt16(b,12+i*2))/0.065535)/1E7);
-                    }
-                    return res.toString();
-                }
-                default:
-                    return String.format("%s (0x%08X): %d bytes", IccReader.getStringFromInt32(iccTagType), iccTagType, bytes.length);
-            }
-        } catch (IOException e) {
-            // TODO decode these values during IccReader.extract so we can report any errors at that time
-            // It is convention to return null if a description cannot be formulated.
-            // If an error is to be reported, it should be done during the extraction process.
-            return null;
-        }
-    }
-
-    @NotNull
-    public static String formatDoubleAsString(double value, int precision, boolean zeroes)
-    {
-        if (precision < 1)
-            return "" + Math.round(value);
-        long intPart = Math.abs((long)value);
-        long rest = (int)Math.round((Math.abs(value) - intPart) * Math.pow(10, precision));
-        long restKept = rest;
-        String res = "";
-        byte cour;
-        for (int i = precision; i > 0; i--) {
-            cour = (byte)(Math.abs(rest % 10));
-            rest /= 10;
-            if (res.length() > 0 || zeroes || cour != 0 || i == 1)
-                res = cour + res;
-        }
-        intPart += rest;
-        boolean isNegative = ((value < 0) && (intPart != 0 || restKept != 0));
-        return (isNegative ? "-" : "") + intPart + "." + res;
-    }
-
-    @Nullable
-    private String getRenderingIntentDescription()
-    {
-        return getIndexedDescription(TAG_RENDERING_INTENT,
-            "Perceptual",
-            "Media-Relative Colorimetric",
-            "Saturation",
-            "ICC-Absolute Colorimetric");
-    }
-
-    @Nullable
-    private String getPlatformDescription()
-    {
-        String str = _directory.getString(TAG_PLATFORM);
-        if (str==null)
-            return null;
-        // Because Java doesn't allow switching on string values, create an integer from the first four chars
-        // and switch on that instead.
-        int i;
-        try {
-            i = getInt32FromString(str);
-        } catch (IOException e) {
-            return str;
-        }
-        switch (i) {
-            case 0x4150504C: // "APPL"
-                return "Apple Computer, Inc.";
-            case 0x4D534654: // "MSFT"
-                return "Microsoft Corporation";
-            case 0x53474920:
-                return "Silicon Graphics, Inc.";
-            case 0x53554E57:
-                return "Sun Microsystems, Inc.";
-            case 0x54474E54:
-                return "Taligent, Inc.";
-            default:
-                return String.format("Unknown (%s)", str);
-        }
-    }
-
-    @Nullable
-    private String getProfileClassDescription()
-    {
-        String str = _directory.getString(TAG_PROFILE_CLASS);
-        if (str==null)
-            return null;
-        // Because Java doesn't allow switching on string values, create an integer from the first four chars
-        // and switch on that instead.
-        int i;
-        try {
-            i = getInt32FromString(str);
-        } catch (IOException e) {
-            return str;
-        }
-        switch (i) {
-            case 0x73636E72:
-                return "Input Device";
-            case 0x6D6E7472: // mntr
-                return "Display Device";
-            case 0x70727472:
-                return "Output Device";
-            case 0x6C696E6B:
-                return "DeviceLink";
-            case 0x73706163:
-                return "ColorSpace Conversion";
-            case 0x61627374:
-                return "Abstract";
-            case 0x6E6D636C:
-                return "Named Color";
-            default:
-                return String.format("Unknown (%s)", str);
-        }
-    }
-
-    @Nullable
-    private String getProfileVersionDescription()
-    {
-        Integer value = _directory.getInteger(TAG_PROFILE_VERSION);
-
-        if (value == null)
-            return null;
-
-        int m = (value & 0xFF000000) >> 24;
-        int r = (value & 0x00F00000) >> 20;
-        int R = (value & 0x000F0000) >> 16;
-
-        return String.format("%d.%d.%d", m, r, R);
-    }
-
-    private static int getInt32FromString(@NotNull String string) throws IOException
-    {
-        byte[] bytes = string.getBytes();
-        return new ByteArrayReader(bytes).getInt32(0);
-    }
-}
Index: unk/src/com/drew/metadata/icc/IccDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/icc/IccDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,208 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.icc;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * @author Yuri Binev
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class IccDirectory extends Directory
-{
-    // These (smaller valued) tags have an integer value that's equal to their offset within the ICC data buffer.
-
-    public static final int TAG_PROFILE_BYTE_COUNT = 0;
-    public static final int TAG_CMM_TYPE = 4;
-    public static final int TAG_PROFILE_VERSION = 8;
-    public static final int TAG_PROFILE_CLASS = 12;
-    public static final int TAG_COLOR_SPACE = 16;
-    public static final int TAG_PROFILE_CONNECTION_SPACE = 20;
-    public static final int TAG_PROFILE_DATETIME = 24;
-    public static final int TAG_SIGNATURE = 36;
-    public static final int TAG_PLATFORM = 40;
-    public static final int TAG_CMM_FLAGS = 44;
-    public static final int TAG_DEVICE_MAKE = 48;
-    public static final int TAG_DEVICE_MODEL = 52;
-    public static final int TAG_DEVICE_ATTR = 56;
-    public static final int TAG_RENDERING_INTENT = 64;
-    public static final int TAG_XYZ_VALUES = 68;
-    public static final int TAG_PROFILE_CREATOR = 80;
-    public static final int TAG_TAG_COUNT = 128;
-
-    // These tag values
-
-    public static final int TAG_TAG_A2B0 = 0x41324230;
-    public static final int TAG_TAG_A2B1 = 0x41324231;
-    public static final int TAG_TAG_A2B2 = 0x41324232;
-    public static final int TAG_TAG_bXYZ = 0x6258595A;
-    public static final int TAG_TAG_bTRC = 0x62545243;
-    public static final int TAG_TAG_B2A0 = 0x42324130;
-    public static final int TAG_TAG_B2A1 = 0x42324131;
-    public static final int TAG_TAG_B2A2 = 0x42324132;
-    public static final int TAG_TAG_calt = 0x63616C74;
-    public static final int TAG_TAG_targ = 0x74617267;
-    public static final int TAG_TAG_chad = 0x63686164;
-    public static final int TAG_TAG_chrm = 0x6368726D;
-    public static final int TAG_TAG_cprt = 0x63707274;
-    public static final int TAG_TAG_crdi = 0x63726469;
-    public static final int TAG_TAG_dmnd = 0x646D6E64;
-    public static final int TAG_TAG_dmdd = 0x646D6464;
-    public static final int TAG_TAG_devs = 0x64657673;
-    public static final int TAG_TAG_gamt = 0x67616D74;
-    public static final int TAG_TAG_kTRC = 0x6B545243;
-    public static final int TAG_TAG_gXYZ = 0x6758595A;
-    public static final int TAG_TAG_gTRC = 0x67545243;
-    public static final int TAG_TAG_lumi = 0x6C756D69;
-    public static final int TAG_TAG_meas = 0x6D656173;
-    public static final int TAG_TAG_bkpt = 0x626B7074;
-    public static final int TAG_TAG_wtpt = 0x77747074;
-    public static final int TAG_TAG_ncol = 0x6E636F6C;
-    public static final int TAG_TAG_ncl2 = 0x6E636C32;
-    public static final int TAG_TAG_resp = 0x72657370;
-    public static final int TAG_TAG_pre0 = 0x70726530;
-    public static final int TAG_TAG_pre1 = 0x70726531;
-    public static final int TAG_TAG_pre2 = 0x70726532;
-    public static final int TAG_TAG_desc = 0x64657363;
-    public static final int TAG_TAG_pseq = 0x70736571;
-    public static final int TAG_TAG_psd0 = 0x70736430;
-    public static final int TAG_TAG_psd1 = 0x70736431;
-    public static final int TAG_TAG_psd2 = 0x70736432;
-    public static final int TAG_TAG_psd3 = 0x70736433;
-    public static final int TAG_TAG_ps2s = 0x70733273;
-    public static final int TAG_TAG_ps2i = 0x70733269;
-    public static final int TAG_TAG_rXYZ = 0x7258595A;
-    public static final int TAG_TAG_rTRC = 0x72545243;
-    public static final int TAG_TAG_scrd = 0x73637264;
-    public static final int TAG_TAG_scrn = 0x7363726E;
-    public static final int TAG_TAG_tech = 0x74656368;
-    public static final int TAG_TAG_bfd = 0x62666420;
-    public static final int TAG_TAG_vued = 0x76756564;
-    public static final int TAG_TAG_view = 0x76696577;
-
-    public static final int TAG_TAG_aabg = 0x61616267;
-    public static final int TAG_TAG_aagg = 0x61616767;
-    public static final int TAG_TAG_aarg = 0x61617267;
-    public static final int TAG_TAG_mmod = 0x6D6D6F64;
-    public static final int TAG_TAG_ndin = 0x6E64696E;
-    public static final int TAG_TAG_vcgt = 0x76636774;
-    public static final int TAG_APPLE_MULTI_LANGUAGE_PROFILE_NAME = 0x6473636d;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_PROFILE_BYTE_COUNT, "Profile Size");
-        _tagNameMap.put(TAG_CMM_TYPE, "CMM Type");
-        _tagNameMap.put(TAG_PROFILE_VERSION, "Version");
-        _tagNameMap.put(TAG_PROFILE_CLASS, "Class");
-        _tagNameMap.put(TAG_COLOR_SPACE, "Color space");
-        _tagNameMap.put(TAG_PROFILE_CONNECTION_SPACE, "Profile Connection Space");
-        _tagNameMap.put(TAG_PROFILE_DATETIME, "Profile Date/Time");
-        _tagNameMap.put(TAG_SIGNATURE, "Signature");
-        _tagNameMap.put(TAG_PLATFORM, "Primary Platform");
-        _tagNameMap.put(TAG_CMM_FLAGS, "CMM Flags");
-        _tagNameMap.put(TAG_DEVICE_MAKE, "Device manufacturer");
-        _tagNameMap.put(TAG_DEVICE_MODEL, "Device model");
-        _tagNameMap.put(TAG_DEVICE_ATTR, "Device attributes");
-        _tagNameMap.put(TAG_RENDERING_INTENT, "Rendering Intent");
-        _tagNameMap.put(TAG_XYZ_VALUES, "XYZ values");
-        _tagNameMap.put(TAG_PROFILE_CREATOR, "Profile Creator");
-        _tagNameMap.put(TAG_TAG_COUNT, "Tag Count");
-        _tagNameMap.put(TAG_TAG_A2B0, "AToB 0");
-        _tagNameMap.put(TAG_TAG_A2B1, "AToB 1");
-        _tagNameMap.put(TAG_TAG_A2B2, "AToB 2");
-        _tagNameMap.put(TAG_TAG_bXYZ, "Blue Colorant");
-        _tagNameMap.put(TAG_TAG_bTRC, "Blue TRC");
-        _tagNameMap.put(TAG_TAG_B2A0, "BToA 0");
-        _tagNameMap.put(TAG_TAG_B2A1, "BToA 1");
-        _tagNameMap.put(TAG_TAG_B2A2, "BToA 2");
-        _tagNameMap.put(TAG_TAG_calt, "Calibration Date/Time");
-        _tagNameMap.put(TAG_TAG_targ, "Char Target");
-        _tagNameMap.put(TAG_TAG_chad, "Chromatic Adaptation");
-        _tagNameMap.put(TAG_TAG_chrm, "Chromaticity");
-        _tagNameMap.put(TAG_TAG_cprt, "Copyright");
-        _tagNameMap.put(TAG_TAG_crdi, "CrdInfo");
-        _tagNameMap.put(TAG_TAG_dmnd, "Device Mfg Description");
-        _tagNameMap.put(TAG_TAG_dmdd, "Device Model Description");
-        _tagNameMap.put(TAG_TAG_devs, "Device Settings");
-        _tagNameMap.put(TAG_TAG_gamt, "Gamut");
-        _tagNameMap.put(TAG_TAG_kTRC, "Gray TRC");
-        _tagNameMap.put(TAG_TAG_gXYZ, "Green Colorant");
-        _tagNameMap.put(TAG_TAG_gTRC, "Green TRC");
-        _tagNameMap.put(TAG_TAG_lumi, "Luminance");
-        _tagNameMap.put(TAG_TAG_meas, "Measurement");
-        _tagNameMap.put(TAG_TAG_bkpt, "Media Black Point");
-        _tagNameMap.put(TAG_TAG_wtpt, "Media White Point");
-        _tagNameMap.put(TAG_TAG_ncol, "Named Color");
-        _tagNameMap.put(TAG_TAG_ncl2, "Named Color 2");
-        _tagNameMap.put(TAG_TAG_resp, "Output Response");
-        _tagNameMap.put(TAG_TAG_pre0, "Preview 0");
-        _tagNameMap.put(TAG_TAG_pre1, "Preview 1");
-        _tagNameMap.put(TAG_TAG_pre2, "Preview 2");
-        _tagNameMap.put(TAG_TAG_desc, "Profile Description");
-        _tagNameMap.put(TAG_TAG_pseq, "Profile Sequence Description");
-        _tagNameMap.put(TAG_TAG_psd0, "Ps2 CRD 0");
-        _tagNameMap.put(TAG_TAG_psd1, "Ps2 CRD 1");
-        _tagNameMap.put(TAG_TAG_psd2, "Ps2 CRD 2");
-        _tagNameMap.put(TAG_TAG_psd3, "Ps2 CRD 3");
-        _tagNameMap.put(TAG_TAG_ps2s, "Ps2 CSA");
-        _tagNameMap.put(TAG_TAG_ps2i, "Ps2 Rendering Intent");
-        _tagNameMap.put(TAG_TAG_rXYZ, "Red Colorant");
-        _tagNameMap.put(TAG_TAG_rTRC, "Red TRC");
-        _tagNameMap.put(TAG_TAG_scrd, "Screening Desc");
-        _tagNameMap.put(TAG_TAG_scrn, "Screening");
-        _tagNameMap.put(TAG_TAG_tech, "Technology");
-        _tagNameMap.put(TAG_TAG_bfd, "Ucrbg");
-        _tagNameMap.put(TAG_TAG_vued, "Viewing Conditions Description");
-        _tagNameMap.put(TAG_TAG_view, "Viewing Conditions");
-        _tagNameMap.put(TAG_TAG_aabg, "Blue Parametric TRC");
-        _tagNameMap.put(TAG_TAG_aagg, "Green Parametric TRC");
-        _tagNameMap.put(TAG_TAG_aarg, "Red Parametric TRC");
-        _tagNameMap.put(TAG_TAG_mmod, "Make And Model");
-        _tagNameMap.put(TAG_TAG_ndin, "Native Display Information");
-        _tagNameMap.put(TAG_TAG_vcgt, "Video Card Gamma");
-        _tagNameMap.put(TAG_APPLE_MULTI_LANGUAGE_PROFILE_NAME, "Apple Multi-language Profile Name");
-    }
-
-    public IccDirectory()
-    {
-        this.setDescriptor(new IccDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "ICC Profile";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/icc/IccReader.java
===================================================================
--- /trunk/src/com/drew/metadata/icc/IccReader.java	(revision 16024)
+++ 	(revision )
@@ -1,217 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.icc;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.DateUtil;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.MetadataReader;
-
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * Reads an ICC profile.
- * <p>
- * More information about ICC:
- * <ul>
- * <li>http://en.wikipedia.org/wiki/ICC_profile</li>
- * <li>http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ICC_Profile.html</li>
- * <li>https://developer.apple.com/library/mac/samplecode/ImageApp/Listings/ICC_h.html</li>
- * </ul>
- *
- * @author Yuri Binev
- * @author Drew Noakes https://drewnoakes.com
- */
-public class IccReader implements JpegSegmentMetadataReader, MetadataReader
-{
-    public static final String JPEG_SEGMENT_PREAMBLE = "ICC_PROFILE";
-
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.APP2);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        final int preambleLength = JPEG_SEGMENT_PREAMBLE.length();
-
-        // ICC data can be spread across multiple JPEG segments.
-        // We concat them together in this buffer for later processing.
-        byte[] buffer = null;
-
-        for (byte[] segmentBytes : segments) {
-            // Skip any segments that do not contain the required preamble
-            if (segmentBytes.length < preambleLength || !JPEG_SEGMENT_PREAMBLE.equalsIgnoreCase(new String(segmentBytes, 0, preambleLength)))
-                continue;
-
-            // NOTE we ignore three bytes here -- are they useful for anything?
-
-            // Grow the buffer
-            if (buffer == null) {
-                buffer = new byte[segmentBytes.length - 14];
-                // skip the first 14 bytes
-                System.arraycopy(segmentBytes, 14, buffer, 0, segmentBytes.length - 14);
-            } else {
-                byte[] newBuffer = new byte[buffer.length + segmentBytes.length - 14];
-                System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
-                System.arraycopy(segmentBytes, 14, newBuffer, buffer.length, segmentBytes.length - 14);
-                buffer = newBuffer;
-            }
-        }
-
-        if (buffer != null)
-            extract(new ByteArrayReader(buffer), metadata);
-    }
-
-    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata)
-    {
-        extract(reader, metadata, null);
-    }
-
-    public void extract(@NotNull final RandomAccessReader reader, @NotNull final Metadata metadata, @Nullable Directory parentDirectory)
-    {
-        // TODO review whether the 'tagPtr' values below really do require RandomAccessReader or whether SequentialReader may be used instead
-
-        IccDirectory directory = new IccDirectory();
-
-        if (parentDirectory != null)
-            directory.setParent(parentDirectory);
-
-        try {
-            int profileByteCount = reader.getInt32(IccDirectory.TAG_PROFILE_BYTE_COUNT);
-            directory.setInt(IccDirectory.TAG_PROFILE_BYTE_COUNT, profileByteCount);
-
-            // For these tags, the int value of the tag is in fact it's offset within the buffer.
-            set4ByteString(directory, IccDirectory.TAG_CMM_TYPE, reader);
-            setInt32(directory, IccDirectory.TAG_PROFILE_VERSION, reader);
-            set4ByteString(directory, IccDirectory.TAG_PROFILE_CLASS, reader);
-            set4ByteString(directory, IccDirectory.TAG_COLOR_SPACE, reader);
-            set4ByteString(directory, IccDirectory.TAG_PROFILE_CONNECTION_SPACE, reader);
-            setDate(directory, IccDirectory.TAG_PROFILE_DATETIME, reader);
-            set4ByteString(directory, IccDirectory.TAG_SIGNATURE, reader);
-            set4ByteString(directory, IccDirectory.TAG_PLATFORM, reader);
-            setInt32(directory, IccDirectory.TAG_CMM_FLAGS, reader);
-            set4ByteString(directory, IccDirectory.TAG_DEVICE_MAKE, reader);
-
-            int temp = reader.getInt32(IccDirectory.TAG_DEVICE_MODEL);
-            if (temp != 0) {
-                if (temp <= 0x20202020) {
-                    directory.setInt(IccDirectory.TAG_DEVICE_MODEL, temp);
-                } else {
-                    directory.setString(IccDirectory.TAG_DEVICE_MODEL, getStringFromInt32(temp));
-                }
-            }
-
-            setInt32(directory, IccDirectory.TAG_RENDERING_INTENT, reader);
-            setInt64(directory, IccDirectory.TAG_DEVICE_ATTR, reader);
-
-            float[] xyz = new float[]{
-                    reader.getS15Fixed16(IccDirectory.TAG_XYZ_VALUES),
-                    reader.getS15Fixed16(IccDirectory.TAG_XYZ_VALUES + 4),
-                    reader.getS15Fixed16(IccDirectory.TAG_XYZ_VALUES + 8)
-            };
-            directory.setObject(IccDirectory.TAG_XYZ_VALUES, xyz);
-
-            // Process 'ICC tags'
-            int tagCount = reader.getInt32(IccDirectory.TAG_TAG_COUNT);
-            directory.setInt(IccDirectory.TAG_TAG_COUNT, tagCount);
-
-            for (int i = 0; i < tagCount; i++) {
-                int pos = IccDirectory.TAG_TAG_COUNT + 4 + i * 12;
-                int tagType = reader.getInt32(pos);
-                int tagPtr = reader.getInt32(pos + 4);
-                int tagLen = reader.getInt32(pos + 8);
-                byte[] b = reader.getBytes(tagPtr, tagLen);
-                directory.setByteArray(tagType, b);
-            }
-        } catch (IOException ex) {
-            directory.addError("Exception reading ICC profile: " + ex.getMessage());
-        }
-
-        metadata.addDirectory(directory);
-    }
-
-    private void set4ByteString(@NotNull Directory directory, int tagType, @NotNull RandomAccessReader reader) throws IOException
-    {
-        int i = reader.getInt32(tagType);
-        if (i != 0)
-            directory.setString(tagType, getStringFromInt32(i));
-    }
-
-    private void setInt32(@NotNull Directory directory, int tagType, @NotNull RandomAccessReader reader) throws IOException
-    {
-        int i = reader.getInt32(tagType);
-        if (i != 0)
-            directory.setInt(tagType, i);
-    }
-
-    @SuppressWarnings({"SameParameterValue"})
-    private void setInt64(@NotNull Directory directory, int tagType, @NotNull RandomAccessReader reader) throws IOException
-    {
-        long l = reader.getInt64(tagType);
-        if (l != 0)
-            directory.setLong(tagType, l);
-    }
-
-    @SuppressWarnings({"SameParameterValue", "MagicConstant"})
-    private void setDate(@NotNull final IccDirectory directory, final int tagType, @NotNull RandomAccessReader reader) throws IOException
-    {
-        final int y = reader.getUInt16(tagType);
-        final int m = reader.getUInt16(tagType + 2);
-        final int d = reader.getUInt16(tagType + 4);
-        final int h = reader.getUInt16(tagType + 6);
-        final int M = reader.getUInt16(tagType + 8);
-        final int s = reader.getUInt16(tagType + 10);
-
-        if (DateUtil.isValidDate(y, m - 1, d) && DateUtil.isValidTime(h, M, s))
-        {
-            String dateString = String.format("%04d:%02d:%02d %02d:%02d:%02d", y, m, d, h, M, s);
-            directory.setString(tagType, dateString);
-        }
-        else
-        {
-            directory.addError(String.format(
-                "ICC data describes an invalid date/time: year=%d month=%d day=%d hour=%d minute=%d second=%d",
-                y, m, d, h, M, s));
-        }
-    }
-
-    @NotNull
-    public static String getStringFromInt32(int d)
-    {
-        // MSB
-        byte[] b = new byte[] {
-                (byte) ((d & 0xFF000000) >> 24),
-                (byte) ((d & 0x00FF0000) >> 16),
-                (byte) ((d & 0x0000FF00) >> 8),
-                (byte) ((d & 0x000000FF))
-        };
-        return new String(b);
-    }
-}
Index: unk/src/com/drew/metadata/icc/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/icc/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of ICC (International Color Consortium) profile metadata.
- */
-package com.drew.metadata.icc;
Index: unk/src/com/drew/metadata/iptc/IptcDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/iptc/IptcDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,332 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.iptc;
-
-import com.drew.lang.StringUtil;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.iptc.IptcDirectory.*;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link IptcDirectory}.
- * <p>
- * As the IPTC directory already stores values as strings, this class simply returns the tag's value.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class IptcDescriptor extends TagDescriptor<IptcDirectory>
-{
-    public IptcDescriptor(@NotNull IptcDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_DATE_CREATED:
-                return getDateCreatedDescription();
-            case TAG_DIGITAL_DATE_CREATED:
-                return getDigitalDateCreatedDescription();
-            case TAG_DATE_SENT:
-                return getDateSentDescription();
-            case TAG_EXPIRATION_DATE:
-                return getExpirationDateDescription();
-            case TAG_EXPIRATION_TIME:
-                return getExpirationTimeDescription();
-            case TAG_FILE_FORMAT:
-                return getFileFormatDescription();
-            case TAG_KEYWORDS:
-                return getKeywordsDescription();
-            case TAG_REFERENCE_DATE:
-                return getReferenceDateDescription();
-            case TAG_RELEASE_DATE:
-                return getReleaseDateDescription();
-            case TAG_RELEASE_TIME:
-                return getReleaseTimeDescription();
-            case TAG_TIME_CREATED:
-                return getTimeCreatedDescription();
-            case TAG_DIGITAL_TIME_CREATED:
-                return getDigitalTimeCreatedDescription();
-            case TAG_TIME_SENT:
-                return getTimeSentDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getDateDescription(int tagType)
-    {
-        String s = _directory.getString(tagType);
-        if (s == null)
-            return null;
-        if (s.length() == 8)
-            return s.substring(0, 4) + ':' + s.substring(4, 6) + ':' + s.substring(6);
-        return s;
-    }
-
-    @Nullable
-    public String getTimeDescription(int tagType)
-    {
-        String s = _directory.getString(tagType);
-        if (s == null)
-            return null;
-        if (s.length() == 6 || s.length() == 11)
-            return s.substring(0, 2) + ':' + s.substring(2, 4) + ':' + s.substring(4);
-        return s;
-    }
-
-    @Nullable
-    public String getFileFormatDescription()
-    {
-        Integer value = _directory.getInteger(TAG_FILE_FORMAT);
-        if (value == null)
-            return null;
-        switch (value) {
-            case 0: return "No ObjectData";
-            case 1: return "IPTC-NAA Digital Newsphoto Parameter Record";
-            case 2: return "IPTC7901 Recommended Message Format";
-            case 3: return "Tagged Image File Format (Adobe/Aldus Image data)";
-            case 4: return "Illustrator (Adobe Graphics data)";
-            case 5: return "AppleSingle (Apple Computer Inc)";
-            case 6: return "NAA 89-3 (ANPA 1312)";
-            case 7: return "MacBinary II";
-            case 8: return "IPTC Unstructured Character Oriented File Format (UCOFF)";
-            case 9: return "United Press International ANPA 1312 variant";
-            case 10: return "United Press International Down-Load Message";
-            case 11: return "JPEG File Interchange (JFIF)";
-            case 12: return "Photo-CD Image-Pac (Eastman Kodak)";
-            case 13: return "Bit Mapped Graphics File [.BMP] (Microsoft)";
-            case 14: return "Digital Audio File [.WAV] (Microsoft & Creative Labs)";
-            case 15: return "Audio plus Moving Video [.AVI] (Microsoft)";
-            case 16: return "PC DOS/Windows Executable Files [.COM][.EXE]";
-            case 17: return "Compressed Binary File [.ZIP] (PKWare Inc)";
-            case 18: return "Audio Interchange File Format AIFF (Apple Computer Inc)";
-            case 19: return "RIFF Wave (Microsoft Corporation)";
-            case 20: return "Freehand (Macromedia/Aldus)";
-            case 21: return "Hypertext Markup Language [.HTML] (The Internet Society)";
-            case 22: return "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC";
-            case 23: return "MPEG 2 Audio Layer 3, ISO/IEC";
-            case 24: return "Portable Document File [.PDF] Adobe";
-            case 25: return "News Industry Text Format (NITF)";
-            case 26: return "Tape Archive [.TAR]";
-            case 27: return "Tidningarnas Telegrambyra NITF version (TTNITF DTD)";
-            case 28: return "Ritzaus Bureau NITF version (RBNITF DTD)";
-            case 29: return "Corel Draw [.CDR]";
-        }
-        return String.format("Unknown (%d)", value);
-    }
-
-    @Nullable
-    public String getByLineDescription()
-    {
-        return _directory.getString(TAG_BY_LINE);
-    }
-
-    @Nullable
-    public String getByLineTitleDescription()
-    {
-        return _directory.getString(TAG_BY_LINE_TITLE);
-    }
-
-    @Nullable
-    public String getCaptionDescription()
-    {
-        return _directory.getString(TAG_CAPTION);
-    }
-
-    @Nullable
-    public String getCategoryDescription()
-    {
-        return _directory.getString(TAG_CATEGORY);
-    }
-
-    @Nullable
-    public String getCityDescription()
-    {
-        return _directory.getString(TAG_CITY);
-    }
-
-    @Nullable
-    public String getCopyrightNoticeDescription()
-    {
-        return _directory.getString(TAG_COPYRIGHT_NOTICE);
-    }
-
-    @Nullable
-    public String getCountryOrPrimaryLocationDescription()
-    {
-        return _directory.getString(TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME);
-    }
-
-    @Nullable
-    public String getCreditDescription()
-    {
-        return _directory.getString(TAG_CREDIT);
-    }
-
-    @Nullable
-    public String getDateCreatedDescription()
-    {
-        return getDateDescription(TAG_DATE_CREATED);
-    }
-
-    @Nullable
-    public String getDigitalDateCreatedDescription()
-    {
-        return getDateDescription(TAG_DIGITAL_DATE_CREATED);
-    }
-
-    @Nullable
-    public String getDateSentDescription()
-    {
-        return getDateDescription(TAG_DATE_SENT);
-    }
-
-    @Nullable
-    public String getExpirationDateDescription()
-    {
-        return getDateDescription(TAG_EXPIRATION_DATE);
-    }
-
-    @Nullable
-    public String getExpirationTimeDescription()
-    {
-        return getTimeDescription(TAG_EXPIRATION_TIME);
-    }
-
-    @Nullable
-    public String getHeadlineDescription()
-    {
-        return _directory.getString(TAG_HEADLINE);
-    }
-
-    @Nullable
-    public String getKeywordsDescription()
-    {
-        final String[] keywords = _directory.getStringArray(TAG_KEYWORDS);
-        if (keywords==null)
-            return null;
-        return StringUtil.join(keywords, ";");
-    }
-
-    @Nullable
-    public String getObjectNameDescription()
-    {
-        return _directory.getString(TAG_OBJECT_NAME);
-    }
-
-    @Nullable
-    public String getOriginalTransmissionReferenceDescription()
-    {
-        return _directory.getString(TAG_ORIGINAL_TRANSMISSION_REFERENCE);
-    }
-
-    @Nullable
-    public String getOriginatingProgramDescription()
-    {
-        return _directory.getString(TAG_ORIGINATING_PROGRAM);
-    }
-
-    @Nullable
-    public String getProvinceOrStateDescription()
-    {
-        return _directory.getString(TAG_PROVINCE_OR_STATE);
-    }
-
-    @Nullable
-    public String getRecordVersionDescription()
-    {
-        return _directory.getString(TAG_APPLICATION_RECORD_VERSION);
-    }
-
-    @Nullable
-    public String getReferenceDateDescription()
-    {
-        return getDateDescription(TAG_REFERENCE_DATE);
-    }
-
-    @Nullable
-    public String getReleaseDateDescription()
-    {
-        return getDateDescription(TAG_RELEASE_DATE);
-    }
-
-    @Nullable
-    public String getReleaseTimeDescription()
-    {
-        return getTimeDescription(TAG_RELEASE_TIME);
-    }
-
-    @Nullable
-    public String getSourceDescription()
-    {
-        return _directory.getString(TAG_SOURCE);
-    }
-
-    @Nullable
-    public String getSpecialInstructionsDescription()
-    {
-        return _directory.getString(TAG_SPECIAL_INSTRUCTIONS);
-    }
-
-    @Nullable
-    public String getSupplementalCategoriesDescription()
-    {
-        return _directory.getString(TAG_SUPPLEMENTAL_CATEGORIES);
-    }
-
-    @Nullable
-    public String getTimeCreatedDescription()
-    {
-        return getTimeDescription(TAG_TIME_CREATED);
-    }
-
-    @Nullable
-    public String getDigitalTimeCreatedDescription()
-    {
-        return getTimeDescription(TAG_DIGITAL_TIME_CREATED);
-    }
-
-    @Nullable
-    public String getTimeSentDescription()
-    {
-        return getTimeDescription(TAG_TIME_SENT);
-    }
-
-    @Nullable
-    public String getUrgencyDescription()
-    {
-        return _directory.getString(TAG_URGENCY);
-    }
-
-    @Nullable
-    public String getWriterDescription()
-    {
-        return _directory.getString(TAG_CAPTION_WRITER);
-    }
-}
Index: unk/src/com/drew/metadata/iptc/IptcDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/iptc/IptcDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,318 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.iptc;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Describes tags used by the International Press Telecommunications Council (IPTC) metadata format.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class IptcDirectory extends Directory
-{
-    // IPTC EnvelopeRecord Tags
-    public static final int TAG_ENVELOPE_RECORD_VERSION          = 0x0100; // 0 + 0x0100
-    public static final int TAG_DESTINATION                      = 0x0105; // 5
-    public static final int TAG_FILE_FORMAT                      = 0x0114; // 20
-    public static final int TAG_FILE_VERSION                     = 0x0116; // 22
-    public static final int TAG_SERVICE_ID                       = 0x011E; // 30
-    public static final int TAG_ENVELOPE_NUMBER                  = 0x0128; // 40
-    public static final int TAG_PRODUCT_ID                       = 0x0132; // 50
-    public static final int TAG_ENVELOPE_PRIORITY                = 0x013C; // 60
-    public static final int TAG_DATE_SENT                        = 0x0146; // 70
-    public static final int TAG_TIME_SENT                        = 0x0150; // 80
-    public static final int TAG_CODED_CHARACTER_SET              = 0x015A; // 90
-    public static final int TAG_UNIQUE_OBJECT_NAME               = 0x0164; // 100
-    public static final int TAG_ARM_IDENTIFIER                   = 0x0178; // 120
-    public static final int TAG_ARM_VERSION                      = 0x017a; // 122
-
-    // IPTC ApplicationRecord Tags
-    public static final int TAG_APPLICATION_RECORD_VERSION       = 0x0200; // 0 + 0x0200
-    public static final int TAG_OBJECT_TYPE_REFERENCE            = 0x0203; // 3
-    public static final int TAG_OBJECT_ATTRIBUTE_REFERENCE       = 0x0204; // 4
-    public static final int TAG_OBJECT_NAME                      = 0x0205; // 5
-    public static final int TAG_EDIT_STATUS                      = 0x0207; // 7
-    public static final int TAG_EDITORIAL_UPDATE                 = 0x0208; // 8
-    public static final int TAG_URGENCY                          = 0X020A; // 10
-    public static final int TAG_SUBJECT_REFERENCE                = 0X020C; // 12
-    public static final int TAG_CATEGORY                         = 0x020F; // 15
-    public static final int TAG_SUPPLEMENTAL_CATEGORIES          = 0x0214; // 20
-    public static final int TAG_FIXTURE_ID                       = 0x0216; // 22
-    public static final int TAG_KEYWORDS                         = 0x0219; // 25
-    public static final int TAG_CONTENT_LOCATION_CODE            = 0x021A; // 26
-    public static final int TAG_CONTENT_LOCATION_NAME            = 0x021B; // 27
-    public static final int TAG_RELEASE_DATE                     = 0X021E; // 30
-    public static final int TAG_RELEASE_TIME                     = 0x0223; // 35
-    public static final int TAG_EXPIRATION_DATE                  = 0x0225; // 37
-    public static final int TAG_EXPIRATION_TIME                  = 0x0226; // 38
-    public static final int TAG_SPECIAL_INSTRUCTIONS             = 0x0228; // 40
-    public static final int TAG_ACTION_ADVISED                   = 0x022A; // 42
-    public static final int TAG_REFERENCE_SERVICE                = 0x022D; // 45
-    public static final int TAG_REFERENCE_DATE                   = 0x022F; // 47
-    public static final int TAG_REFERENCE_NUMBER                 = 0x0232; // 50
-    public static final int TAG_DATE_CREATED                     = 0x0237; // 55
-    public static final int TAG_TIME_CREATED                     = 0X023C; // 60
-    public static final int TAG_DIGITAL_DATE_CREATED             = 0x023E; // 62
-    public static final int TAG_DIGITAL_TIME_CREATED             = 0x023F; // 63
-    public static final int TAG_ORIGINATING_PROGRAM              = 0x0241; // 65
-    public static final int TAG_PROGRAM_VERSION                  = 0x0246; // 70
-    public static final int TAG_OBJECT_CYCLE                     = 0x024B; // 75
-    public static final int TAG_BY_LINE                          = 0x0250; // 80
-    public static final int TAG_BY_LINE_TITLE                    = 0x0255; // 85
-    public static final int TAG_CITY                             = 0x025A; // 90
-    public static final int TAG_SUB_LOCATION                     = 0x025C; // 92
-    public static final int TAG_PROVINCE_OR_STATE                = 0x025F; // 95
-    public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE = 0x0264; // 100
-    public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME = 0x0265; // 101
-    public static final int TAG_ORIGINAL_TRANSMISSION_REFERENCE  = 0x0267; // 103
-    public static final int TAG_HEADLINE                         = 0x0269; // 105
-    public static final int TAG_CREDIT                           = 0x026E; // 110
-    public static final int TAG_SOURCE                           = 0x0273; // 115
-    public static final int TAG_COPYRIGHT_NOTICE                 = 0x0274; // 116
-    public static final int TAG_CONTACT                          = 0x0276; // 118
-    public static final int TAG_CAPTION                          = 0x0278; // 120
-    public static final int TAG_LOCAL_CAPTION                    = 0x0279; // 121
-    public static final int TAG_CAPTION_WRITER                   = 0x027A; // 122
-    public static final int TAG_RASTERIZED_CAPTION               = 0x027D; // 125
-    public static final int TAG_IMAGE_TYPE                       = 0x0282; // 130
-    public static final int TAG_IMAGE_ORIENTATION                = 0x0283; // 131
-    public static final int TAG_LANGUAGE_IDENTIFIER              = 0x0287; // 135
-    public static final int TAG_AUDIO_TYPE                       = 0x0296; // 150
-    public static final int TAG_AUDIO_SAMPLING_RATE              = 0x0297; // 151
-    public static final int TAG_AUDIO_SAMPLING_RESOLUTION        = 0x0298; // 152
-    public static final int TAG_AUDIO_DURATION                   = 0x0299; // 153
-    public static final int TAG_AUDIO_OUTCUE                     = 0x029A; // 154
-
-    public static final int TAG_JOB_ID                           = 0x02B8; // 184
-    public static final int TAG_MASTER_DOCUMENT_ID               = 0x02B9; // 185
-    public static final int TAG_SHORT_DOCUMENT_ID                = 0x02BA; // 186
-    public static final int TAG_UNIQUE_DOCUMENT_ID               = 0x02BB; // 187
-    public static final int TAG_OWNER_ID                         = 0x02BC; // 188
-
-    public static final int TAG_OBJECT_PREVIEW_FILE_FORMAT       = 0x02C8; // 200
-    public static final int TAG_OBJECT_PREVIEW_FILE_FORMAT_VERSION  = 0x02C9; // 201
-    public static final int TAG_OBJECT_PREVIEW_DATA              = 0x02CA; // 202
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_ENVELOPE_RECORD_VERSION, "Enveloped Record Version");
-        _tagNameMap.put(TAG_DESTINATION, "Destination");
-        _tagNameMap.put(TAG_FILE_FORMAT, "File Format");
-        _tagNameMap.put(TAG_FILE_VERSION, "File Version");
-        _tagNameMap.put(TAG_SERVICE_ID, "Service Identifier");
-        _tagNameMap.put(TAG_ENVELOPE_NUMBER, "Envelope Number");
-        _tagNameMap.put(TAG_PRODUCT_ID, "Product Identifier");
-        _tagNameMap.put(TAG_ENVELOPE_PRIORITY, "Envelope Priority");
-        _tagNameMap.put(TAG_DATE_SENT, "Date Sent");
-        _tagNameMap.put(TAG_TIME_SENT, "Time Sent");
-        _tagNameMap.put(TAG_CODED_CHARACTER_SET, "Coded Character Set");
-        _tagNameMap.put(TAG_UNIQUE_OBJECT_NAME, "Unique Object Name");
-        _tagNameMap.put(TAG_ARM_IDENTIFIER, "ARM Identifier");
-        _tagNameMap.put(TAG_ARM_VERSION, "ARM Version");
-
-        _tagNameMap.put(TAG_APPLICATION_RECORD_VERSION, "Application Record Version");
-        _tagNameMap.put(TAG_OBJECT_TYPE_REFERENCE, "Object Type Reference");
-        _tagNameMap.put(TAG_OBJECT_ATTRIBUTE_REFERENCE, "Object Attribute Reference");
-        _tagNameMap.put(TAG_OBJECT_NAME, "Object Name");
-        _tagNameMap.put(TAG_EDIT_STATUS, "Edit Status");
-        _tagNameMap.put(TAG_EDITORIAL_UPDATE, "Editorial Update");
-        _tagNameMap.put(TAG_URGENCY, "Urgency");
-        _tagNameMap.put(TAG_SUBJECT_REFERENCE, "Subject Reference");
-        _tagNameMap.put(TAG_CATEGORY, "Category");
-        _tagNameMap.put(TAG_SUPPLEMENTAL_CATEGORIES, "Supplemental Category(s)");
-        _tagNameMap.put(TAG_FIXTURE_ID, "Fixture Identifier");
-        _tagNameMap.put(TAG_KEYWORDS, "Keywords");
-        _tagNameMap.put(TAG_CONTENT_LOCATION_CODE, "Content Location Code");
-        _tagNameMap.put(TAG_CONTENT_LOCATION_NAME, "Content Location Name");
-        _tagNameMap.put(TAG_RELEASE_DATE, "Release Date");
-        _tagNameMap.put(TAG_RELEASE_TIME, "Release Time");
-        _tagNameMap.put(TAG_EXPIRATION_DATE, "Expiration Date");
-        _tagNameMap.put(TAG_EXPIRATION_TIME, "Expiration Time");
-        _tagNameMap.put(TAG_SPECIAL_INSTRUCTIONS, "Special Instructions");
-        _tagNameMap.put(TAG_ACTION_ADVISED, "Action Advised");
-        _tagNameMap.put(TAG_REFERENCE_SERVICE, "Reference Service");
-        _tagNameMap.put(TAG_REFERENCE_DATE, "Reference Date");
-        _tagNameMap.put(TAG_REFERENCE_NUMBER, "Reference Number");
-        _tagNameMap.put(TAG_DATE_CREATED, "Date Created");
-        _tagNameMap.put(TAG_TIME_CREATED, "Time Created");
-        _tagNameMap.put(TAG_DIGITAL_DATE_CREATED, "Digital Date Created");
-        _tagNameMap.put(TAG_DIGITAL_TIME_CREATED, "Digital Time Created");
-        _tagNameMap.put(TAG_ORIGINATING_PROGRAM, "Originating Program");
-        _tagNameMap.put(TAG_PROGRAM_VERSION, "Program Version");
-        _tagNameMap.put(TAG_OBJECT_CYCLE, "Object Cycle");
-        _tagNameMap.put(TAG_BY_LINE, "By-line");
-        _tagNameMap.put(TAG_BY_LINE_TITLE, "By-line Title");
-        _tagNameMap.put(TAG_CITY, "City");
-        _tagNameMap.put(TAG_SUB_LOCATION, "Sub-location");
-        _tagNameMap.put(TAG_PROVINCE_OR_STATE, "Province/State");
-        _tagNameMap.put(TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE, "Country/Primary Location Code");
-        _tagNameMap.put(TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME, "Country/Primary Location Name");
-        _tagNameMap.put(TAG_ORIGINAL_TRANSMISSION_REFERENCE, "Original Transmission Reference");
-        _tagNameMap.put(TAG_HEADLINE, "Headline");
-        _tagNameMap.put(TAG_CREDIT, "Credit");
-        _tagNameMap.put(TAG_SOURCE, "Source");
-        _tagNameMap.put(TAG_COPYRIGHT_NOTICE, "Copyright Notice");
-        _tagNameMap.put(TAG_CONTACT, "Contact");
-        _tagNameMap.put(TAG_CAPTION, "Caption/Abstract");
-        _tagNameMap.put(TAG_LOCAL_CAPTION, "Local Caption");
-        _tagNameMap.put(TAG_CAPTION_WRITER, "Caption Writer/Editor");
-        _tagNameMap.put(TAG_RASTERIZED_CAPTION, "Rasterized Caption");
-        _tagNameMap.put(TAG_IMAGE_TYPE, "Image Type");
-        _tagNameMap.put(TAG_IMAGE_ORIENTATION, "Image Orientation");
-        _tagNameMap.put(TAG_LANGUAGE_IDENTIFIER, "Language Identifier");
-        _tagNameMap.put(TAG_AUDIO_TYPE, "Audio Type");
-        _tagNameMap.put(TAG_AUDIO_SAMPLING_RATE, "Audio Sampling Rate");
-        _tagNameMap.put(TAG_AUDIO_SAMPLING_RESOLUTION, "Audio Sampling Resolution");
-        _tagNameMap.put(TAG_AUDIO_DURATION, "Audio Duration");
-        _tagNameMap.put(TAG_AUDIO_OUTCUE, "Audio Outcue");
-
-        _tagNameMap.put(TAG_JOB_ID, "Job Identifier");
-        _tagNameMap.put(TAG_MASTER_DOCUMENT_ID, "Master Document Identifier");
-        _tagNameMap.put(TAG_SHORT_DOCUMENT_ID, "Short Document Identifier");
-        _tagNameMap.put(TAG_UNIQUE_DOCUMENT_ID, "Unique Document Identifier");
-        _tagNameMap.put(TAG_OWNER_ID, "Owner Identifier");
-
-        _tagNameMap.put(TAG_OBJECT_PREVIEW_FILE_FORMAT, "Object Data Preview File Format");
-        _tagNameMap.put(TAG_OBJECT_PREVIEW_FILE_FORMAT_VERSION, "Object Data Preview File Format Version");
-        _tagNameMap.put(TAG_OBJECT_PREVIEW_DATA, "Object Data Preview Data");
-    }
-
-    public IptcDirectory()
-    {
-        this.setDescriptor(new IptcDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "IPTC";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    /**
-     * Returns any keywords contained in the IPTC data.  This value may be <code>null</code>.
-     */
-    @Nullable
-    public List<String> getKeywords()
-    {
-        final String[] array = getStringArray(TAG_KEYWORDS);
-        if (array==null)
-            return null;
-        return Arrays.asList(array);
-    }
-
-    /**
-     * Parses the Date Sent tag and the Time Sent tag to obtain a single Date object representing the
-     * date and time when the service sent this image.
-     * @return A Date object representing when the service sent this image, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateSent()
-    {
-        return getDate(TAG_DATE_SENT, TAG_TIME_SENT);
-    }
-
-    /**
-     * Parses the Release Date tag and the Release Time tag to obtain a single Date object representing the
-     * date and time when this image was released.
-     * @return A Date object representing when this image was released, if possible, otherwise null
-     */
-    @Nullable
-    public Date getReleaseDate()
-    {
-        return getDate(TAG_RELEASE_DATE, TAG_RELEASE_TIME);
-    }
-
-    /**
-     * Parses the Expiration Date tag and the Expiration Time tag to obtain a single Date object representing
-     * that this image should not used after this date and time.
-     * @return A Date object representing when this image was released, if possible, otherwise null
-     */
-    @Nullable
-    public Date getExpirationDate()
-    {
-        return getDate(TAG_EXPIRATION_DATE, TAG_EXPIRATION_TIME);
-    }
-
-    /**
-     * Parses the Date Created tag and the Time Created tag to obtain a single Date object representing the
-     * date and time when this image was captured.
-     * @return A Date object representing when this image was captured, if possible, otherwise null
-     */
-    @Nullable
-    public Date getDateCreated()
-    {
-        return getDate(TAG_DATE_CREATED, TAG_TIME_CREATED);
-    }
-
-    /**
-     * Parses the Digital Date Created tag and the Digital Time Created tag to obtain a single Date object
-     * representing the date and time when the digital representation of this image was created.
-     * @return A Date object representing when the digital representation of this image was created,
-     * if possible, otherwise null
-     */
-    @Nullable
-    public Date getDigitalDateCreated()
-    {
-        return getDate(TAG_DIGITAL_DATE_CREATED, TAG_DIGITAL_TIME_CREATED);
-    }
-
-    @Nullable
-    private Date getDate(int dateTagType, int timeTagType)
-    {
-        String date = getString(dateTagType);
-        String time = getString(timeTagType);
-
-        if (date == null)
-            return null;
-        if (time == null)
-            return null;
-
-        try {
-            DateFormat parser = new SimpleDateFormat("yyyyMMddHHmmssZ");
-            return parser.parse(date + time);
-        } catch (ParseException e) {
-            return null;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/iptc/IptcReader.java
===================================================================
--- /trunk/src/com/drew/metadata/iptc/IptcReader.java	(revision 16024)
+++ 	(revision )
@@ -1,241 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.iptc;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.StringValue;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.Collections;
-
-/**
- * Decodes IPTC binary data, populating a {@link Metadata} object with tag values in an {@link IptcDirectory}.
- * <p>
- * http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class IptcReader implements JpegSegmentMetadataReader
-{
-    // TODO consider breaking the IPTC section up into multiple directories and providing segregation of each IPTC directory
-/*
-    public static final int DIRECTORY_IPTC = 2;
-
-    public static final int ENVELOPE_RECORD = 1;
-    public static final int APPLICATION_RECORD_2 = 2;
-    public static final int APPLICATION_RECORD_3 = 3;
-    public static final int APPLICATION_RECORD_4 = 4;
-    public static final int APPLICATION_RECORD_5 = 5;
-    public static final int APPLICATION_RECORD_6 = 6;
-    public static final int PRE_DATA_RECORD = 7;
-    public static final int DATA_RECORD = 8;
-    public static final int POST_DATA_RECORD = 9;
-*/
-    private static final byte IptcMarkerByte = 0x1c;
-
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.APPD);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        for (byte[] segmentBytes : segments) {
-            // Ensure data starts with the IPTC marker byte
-            if (segmentBytes.length != 0 && segmentBytes[0] == IptcMarkerByte) {
-                extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.length);
-            }
-        }
-    }
-
-    /**
-     * Performs the IPTC data extraction, adding found values to the specified instance of {@link Metadata}.
-     */
-    public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length)
-    {
-        extract(reader, metadata, length, null);
-    }
-
-    /**
-     * Performs the IPTC data extraction, adding found values to the specified instance of {@link Metadata}.
-     */
-    public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata, long length, @Nullable Directory parentDirectory)
-    {
-        IptcDirectory directory = new IptcDirectory();
-        metadata.addDirectory(directory);
-
-        if (parentDirectory != null)
-            directory.setParent(parentDirectory);
-
-        int offset = 0;
-
-        // for each tag
-        while (offset < length) {
-
-            // identifies start of a tag
-            short startByte;
-            try {
-                startByte = reader.getUInt8();
-                offset++;
-            } catch (IOException e) {
-                directory.addError("Unable to read starting byte of IPTC tag");
-                return;
-            }
-
-            if (startByte != IptcMarkerByte) {
-                // NOTE have seen images where there was one extra byte at the end, giving
-                // offset==length at this point, which is not worth logging as an error.
-                if (offset != length)
-                    directory.addError("Invalid IPTC tag marker at offset " + (offset - 1) + ". Expected '0x" + Integer.toHexString(IptcMarkerByte) + "' but got '0x" + Integer.toHexString(startByte) + "'.");
-                return;
-            }
-
-            // we need at least four bytes left to read a tag
-            if (offset + 4 > length) {
-                directory.addError("Too few bytes remain for a valid IPTC tag");
-                return;
-            }
-
-            int directoryType;
-            int tagType;
-            int tagByteCount;
-            try {
-                directoryType = reader.getUInt8();
-                tagType = reader.getUInt8();
-                tagByteCount = reader.getUInt16();
-                if (tagByteCount > 32767) {
-                    // Extended DataSet Tag (see 1.5(c), p14, IPTC-IIMV4.2.pdf)
-                    tagByteCount = ((tagByteCount & 0x7FFF) << 16) | reader.getUInt16();
-                    offset += 2;
-                }
-                offset += 4;
-            } catch (IOException e) {
-                directory.addError("IPTC data segment ended mid-way through tag descriptor");
-                return;
-            }
-
-            if (offset + tagByteCount > length) {
-                directory.addError("Data for tag extends beyond end of IPTC segment");
-                return;
-            }
-
-            try {
-                processTag(reader, directory, directoryType, tagType, tagByteCount);
-            } catch (IOException e) {
-                directory.addError("Error processing IPTC tag");
-                return;
-            }
-
-            offset += tagByteCount;
-        }
-    }
-
-    private void processTag(@NotNull SequentialReader reader, @NotNull Directory directory, int directoryType, int tagType, int tagByteCount) throws IOException
-    {
-        int tagIdentifier = tagType | (directoryType << 8);
-
-        // Some images have been seen that specify a zero byte tag, which cannot be of much use.
-        // We elect here to completely ignore the tag. The IPTC specification doesn't mention
-        // anything about the interpretation of this situation.
-        // https://raw.githubusercontent.com/wiki/drewnoakes/metadata-extractor/docs/IPTC-IIMV4.2.pdf
-        if (tagByteCount == 0) {
-            directory.setString(tagIdentifier, "");
-            return;
-        }
-
-        switch (tagIdentifier) {
-            case IptcDirectory.TAG_CODED_CHARACTER_SET:
-                byte[] bytes = reader.getBytes(tagByteCount);
-                String charsetName = Iso2022Converter.convertISO2022CharsetToJavaCharset(bytes);
-                if (charsetName == null) {
-                    // Unable to determine the charset, so fall through and treat tag as a regular string
-                    charsetName = new String(bytes);
-                }
-                directory.setString(tagIdentifier, charsetName);
-                return;
-            case IptcDirectory.TAG_ENVELOPE_RECORD_VERSION:
-            case IptcDirectory.TAG_APPLICATION_RECORD_VERSION:
-            case IptcDirectory.TAG_FILE_VERSION:
-            case IptcDirectory.TAG_ARM_VERSION:
-            case IptcDirectory.TAG_PROGRAM_VERSION:
-                // short
-                if (tagByteCount >= 2) {
-                    int shortValue = reader.getUInt16();
-                    reader.skip(tagByteCount - 2);
-                    directory.setInt(tagIdentifier, shortValue);
-                    return;
-                }
-                break;
-            case IptcDirectory.TAG_URGENCY:
-                // byte
-                directory.setInt(tagIdentifier, reader.getUInt8());
-                reader.skip(tagByteCount - 1);
-                return;
-            default:
-                // fall through
-        }
-
-        // If we haven't returned yet, treat it as a string
-        // NOTE that there's a chance we've already loaded the value as a string above, but failed to parse the value
-        String charSetName = directory.getString(IptcDirectory.TAG_CODED_CHARACTER_SET);
-        Charset charset = null;
-        try {
-            if (charSetName != null)
-                charset = Charset.forName(charSetName);
-        } catch (Throwable ignored) {
-        }
-
-        StringValue string;
-        if (charSetName != null) {
-            string = reader.getStringValue(tagByteCount, charset);
-        } else {
-            byte[] bytes = reader.getBytes(tagByteCount);
-            Charset charSet = Iso2022Converter.guessCharSet(bytes);
-            string = charSet != null ? new StringValue(bytes, charSet) : new StringValue(bytes, null);
-        }
-
-        if (directory.containsTag(tagIdentifier)) {
-            // this fancy StringValue[] business avoids using an ArrayList for performance reasons
-            StringValue[] oldStrings = directory.getStringValueArray(tagIdentifier);
-            StringValue[] newStrings;
-            if (oldStrings == null) {
-                // TODO hitting this block means any prior value(s) are discarded
-                newStrings = new StringValue[1];
-            } else {
-                newStrings = new StringValue[oldStrings.length + 1];
-                System.arraycopy(oldStrings, 0, newStrings, 0, oldStrings.length);
-            }
-            newStrings[newStrings.length - 1] = string;
-            directory.setStringValueArray(tagIdentifier, newStrings);
-        } else {
-            directory.setStringValue(tagIdentifier, string);
-        }
-    }
-}
Index: unk/src/com/drew/metadata/iptc/Iso2022Converter.java
===================================================================
--- /trunk/src/com/drew/metadata/iptc/Iso2022Converter.java	(revision 16024)
+++ 	(revision )
@@ -1,104 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.iptc;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-
-public final class Iso2022Converter
-{
-    private static final String ISO_8859_1 = "ISO-8859-1";
-    private static final String UTF_8 = "UTF-8";
-
-    private static final byte LATIN_CAPITAL_A = 0x41;
-    private static final int DOT = 0xe280a2;
-    private static final byte LATIN_CAPITAL_G = 0x47;
-    private static final byte PERCENT_SIGN = 0x25;
-    private static final byte ESC = 0x1B;
-
-    /**
-     * Converts the given ISO2022 char set to a Java charset name.
-     *
-     * @param bytes string data encoded using ISO2022
-     * @return the Java charset name as a string, or <code>null</code> if the conversion was not possible
-     */
-    @Nullable
-    public static String convertISO2022CharsetToJavaCharset(@NotNull final byte[] bytes)
-    {
-        if (bytes.length > 2 && bytes[0] == ESC && bytes[1] == PERCENT_SIGN && bytes[2] == LATIN_CAPITAL_G)
-            return UTF_8;
-
-        if (bytes.length > 3 && bytes[0] == ESC && (bytes[3] & 0xFF | ((bytes[2] & 0xFF) << 8) | ((bytes[1] & 0xFF) << 16)) == DOT && bytes[4] == LATIN_CAPITAL_A)
-            return ISO_8859_1;
-
-        return null;
-    }
-
-    /**
-     * Attempts to guess the {@link Charset} of a string provided as a byte array.
-     * <p>
-     * Charsets trialled are, in order:
-     * <ul>
-     *     <li>UTF-8</li>
-     *     <li><code>System.getProperty("file.encoding")</code></li>
-     *     <li>ISO-8859-1</li>
-     * </ul>
-     * <p>
-     * Its only purpose is to guess the Charset if and only if IPTC tag coded character set is not set. If the
-     * encoding is not UTF-8, the tag should be set. Otherwise it is bad practice. This method tries to
-     * workaround this issue since some metadata manipulating tools do not prevent such bad practice.
-     * <p>
-     * About the reliability of this method: The check if some bytes are UTF-8 or not has a very high reliability.
-     * The two other checks are less reliable.
-     *
-     * @param bytes some text as bytes
-     * @return the name of the encoding or null if none could be guessed
-     */
-    @Nullable
-    static Charset guessCharSet(@NotNull final byte[] bytes)
-    {
-        String[] encodings = { UTF_8, System.getProperty("file.encoding"), ISO_8859_1 };
-
-        for (String encoding : encodings)
-        {
-            Charset charset = Charset.forName(encoding);
-            CharsetDecoder cs = charset.newDecoder();
-
-            try {
-                cs.decode(ByteBuffer.wrap(bytes));
-                return charset;
-            } catch (CharacterCodingException e) {
-                // fall through...
-            }
-        }
-
-        // No encodings succeeded. Return null.
-        return null;
-    }
-
-    private Iso2022Converter()
-    {}
-}
Index: unk/src/com/drew/metadata/iptc/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/iptc/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of IPTC metadata.
- */
-package com.drew.metadata.iptc;
Index: unk/src/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/HuffmanTablesDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.jpeg.HuffmanTablesDirectory.*;
-
-/**
- * Provides a human-readable string version of the tag stored in a {@link HuffmanTablesDirectory}.
- *
- * <ul>
- *   <li>https://en.wikipedia.org/wiki/Huffman_coding</li>
- *   <li>http://stackoverflow.com/a/4954117</li>
- * </ul>
- *
- * @author Nadahar
- */
-@SuppressWarnings("WeakerAccess")
-public class HuffmanTablesDescriptor extends TagDescriptor<HuffmanTablesDirectory>
-{
-    public HuffmanTablesDescriptor(@NotNull HuffmanTablesDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_NUMBER_OF_TABLES:
-                return getNumberOfTablesDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getNumberOfTablesDescription()
-    {
-        Integer value = _directory.getInteger(TAG_NUMBER_OF_TABLES);
-        if (value == null)
-            return null;
-        return value + (value == 1 ? " Huffman table" : " Huffman tables");
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/HuffmanTablesDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/HuffmanTablesDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,363 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-import com.drew.metadata.MetadataException;
-
-/**
- * Directory of tables for the DHT (Define Huffman Table(s)) segment.
- *
- * @author Nadahar
- */
-@SuppressWarnings("WeakerAccess")
-public class HuffmanTablesDirectory extends Directory {
-
-    public static final int TAG_NUMBER_OF_TABLES = 1;
-
-    protected static final byte[] TYPICAL_LUMINANCE_DC_LENGTHS = {
-        (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
-        (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
-    };
-
-    protected static final byte[] TYPICAL_LUMINANCE_DC_VALUES = {
-        (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
-        (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B
-    };
-
-    protected static final byte[] TYPICAL_CHROMINANCE_DC_LENGTHS = {
-        (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
-        (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
-    };
-
-    protected static final byte[] TYPICAL_CHROMINANCE_DC_VALUES = {
-        (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
-        (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B
-    };
-
-    protected static final byte[] TYPICAL_LUMINANCE_AC_LENGTHS = {
-        (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x03, (byte) 0x03, (byte) 0x02, (byte) 0x04, (byte) 0x03,
-        (byte) 0x05, (byte) 0x05, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x7D
-    };
-
-    protected static final byte[] TYPICAL_LUMINANCE_AC_VALUES = {
-        (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0x11, (byte) 0x05, (byte) 0x12,
-        (byte) 0x21, (byte) 0x31, (byte) 0x41, (byte) 0x06, (byte) 0x13, (byte) 0x51, (byte) 0x61, (byte) 0x07,
-        (byte) 0x22, (byte) 0x71, (byte) 0x14, (byte) 0x32, (byte) 0x81, (byte) 0x91, (byte) 0xA1, (byte) 0x08,
-        (byte) 0x23, (byte) 0x42, (byte) 0xB1, (byte) 0xC1, (byte) 0x15, (byte) 0x52, (byte) 0xD1, (byte) 0xF0,
-        (byte) 0x24, (byte) 0x33, (byte) 0x62, (byte) 0x72, (byte) 0x82, (byte) 0x09, (byte) 0x0A, (byte) 0x16,
-        (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28,
-        (byte) 0x29, (byte) 0x2A, (byte) 0x34, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39,
-        (byte) 0x3A, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49,
-        (byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59,
-        (byte) 0x5A, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69,
-        (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79,
-        (byte) 0x7A, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89,
-        (byte) 0x8A, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98,
-        (byte) 0x99, (byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7,
-        (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5, (byte) 0xB6,
-        (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5,
-        (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4,
-        (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xE1, (byte) 0xE2,
-        (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7, (byte) 0xE8, (byte) 0xE9, (byte) 0xEA,
-        (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8,
-        (byte) 0xF9, (byte) 0xFA
-    };
-
-    protected static final byte[] TYPICAL_CHROMINANCE_AC_LENGTHS = {
-        (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x04, (byte) 0x03, (byte) 0x04,
-        (byte) 0x07, (byte) 0x05, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x77
-    };
-
-    protected static final byte[] TYPICAL_CHROMINANCE_AC_VALUES = {
-        (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x11, (byte) 0x04, (byte) 0x05, (byte) 0x21,
-        (byte) 0x31, (byte) 0x06, (byte) 0x12, (byte) 0x41, (byte) 0x51, (byte) 0x07, (byte) 0x61, (byte) 0x71,
-        (byte) 0x13, (byte) 0x22, (byte) 0x32, (byte) 0x81, (byte) 0x08, (byte) 0x14, (byte) 0x42, (byte) 0x91,
-        (byte) 0xA1, (byte) 0xB1, (byte) 0xC1, (byte) 0x09, (byte) 0x23, (byte) 0x33, (byte) 0x52, (byte) 0xF0,
-        (byte) 0x15, (byte) 0x62, (byte) 0x72, (byte) 0xD1, (byte) 0x0A, (byte) 0x16, (byte) 0x24, (byte) 0x34,
-        (byte) 0xE1, (byte) 0x25, (byte) 0xF1, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x26,
-        (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2A, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38,
-        (byte) 0x39, (byte) 0x3A, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48,
-        (byte) 0x49, (byte) 0x4A, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58,
-        (byte) 0x59, (byte) 0x5A, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68,
-        (byte) 0x69, (byte) 0x6A, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78,
-        (byte) 0x79, (byte) 0x7A, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87,
-        (byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96,
-        (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x9A, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5,
-        (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4,
-        (byte) 0xB5, (byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xC2, (byte) 0xC3,
-        (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xD2,
-        (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA,
-        (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7, (byte) 0xE8, (byte) 0xE9,
-        (byte) 0xEA, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8,
-        (byte) 0xF9, (byte) 0xFA
-    };
-
-    @NotNull
-    protected final List<HuffmanTable> tables = new ArrayList<HuffmanTable>(4);
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static
-    {
-        _tagNameMap.put(TAG_NUMBER_OF_TABLES, "Number of Tables");
-    }
-
-    public HuffmanTablesDirectory()
-    {
-        this.setDescriptor(new HuffmanTablesDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Huffman";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    /**
-     * @param tableNumber The zero-based index of the table. This number is normally between 0 and 3.
-     *                    Use {@link #getNumberOfTables} for bounds-checking.
-     * @return The {@link HuffmanTable} having the specified number.
-     */
-    @NotNull
-    public HuffmanTable getTable(int tableNumber)
-    {
-        return tables.get(tableNumber);
-    }
-
-    /**
-     * @return The number of Huffman tables held by this {@link HuffmanTablesDirectory} instance.
-     */
-    public int getNumberOfTables() throws MetadataException
-    {
-        return getInt(HuffmanTablesDirectory.TAG_NUMBER_OF_TABLES);
-    }
-
-    /**
-     * @return The {@link List} of {@link HuffmanTable}s in this
-     * {@link Directory}.
-     */
-    @NotNull
-    protected List<HuffmanTable> getTables() {
-        return tables;
-    }
-
-    /**
-     * Evaluates whether all the tables in this {@link HuffmanTablesDirectory}
-     * are "typical" Huffman tables.
-     * <p>
-     * "Typical" has a special meaning in this context as the JPEG standard
-     * (ISO/IEC 10918 or ITU-T T.81) defines 4 Huffman tables that has been
-     * developed from the average statistics of a large set of images with 8-bit
-     * precision. Using these instead of calculating the optimal Huffman tables
-     * for a given image is faster, and is preferred by many hardware encoders
-     * and some hardware decoders.
-     * <p>
-     * Even though the JPEG standard doesn't define these as "standard tables"
-     * and requires a decoder to be able to read any valid Huffman tables, some
-     * are in reality limited decoding images using these "typical" tables.
-     * Standards like DCF (Design rule for Camera File system) and DLNA (Digital
-     * Living Network Alliance) actually requires any compliant JPEG to use only
-     * the "typical" Huffman tables.
-     * <p>
-     * This is also related to the term "optimized" JPEG. An "optimized" JPEG is
-     * a JPEG that doesn't use the "typical" Huffman tables.
-     *
-     * @return Whether or not all the tables in this
-     *         {@link HuffmanTablesDirectory} are the predefined "typical"
-     *         Huffman tables.
-     */
-    public boolean isTypical() {
-        if (tables.size() == 0) {
-            return false;
-        }
-        for (HuffmanTable table : tables) {
-            if (!table.isTypical()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * The opposite of {@link #isTypical()}.
-     *
-     * @return Whether or not the tables in this {@link HuffmanTablesDirectory}
-     *         are "optimized" - which means that at least one of them aren't
-     *         one of the "typical" Huffman tables.
-     */
-    public boolean isOptimized() {
-        return !isTypical();
-    }
-
-    /**
-     * An instance of this class holds a JPEG Huffman table.
-     */
-    public static class HuffmanTable {
-        private final int _tableLength;
-        private final HuffmanTableClass _tableClass;
-        private final int _tableDestinationId;
-        private final byte[] _lengthBytes;
-        private final byte[] _valueBytes;
-
-        @SuppressWarnings("ConstantConditions")
-        public HuffmanTable(
-            @NotNull HuffmanTableClass tableClass,
-            int tableDestinationId,
-            @NotNull byte[] lengthBytes,
-            @NotNull byte[] valueBytes)
-        {
-            if (lengthBytes == null)
-                throw new IllegalArgumentException("lengthBytes cannot be null.");
-            if (valueBytes == null)
-                throw new IllegalArgumentException("valueBytes cannot be null.");
-
-            _tableClass = tableClass;
-            _tableDestinationId = tableDestinationId;
-            _lengthBytes = lengthBytes;
-            _valueBytes = valueBytes;
-            _tableLength = _valueBytes.length + 17;
-        }
-
-        /**
-         * @return The table length in bytes.
-         */
-        public int getTableLength() {
-            return _tableLength;
-        }
-
-        /**
-         * @return The {@link HuffmanTableClass} of this table.
-         */
-        public HuffmanTableClass getTableClass() {
-            return _tableClass;
-        }
-
-        /**
-         * @return the the destination identifier for this table.
-         */
-        public int getTableDestinationId() {
-            return _tableDestinationId;
-        }
-
-        /**
-         * @return A byte array with the L values for this table.
-         */
-        @NotNull
-        public byte[] getLengthBytes() {
-            byte[] result = new byte[_lengthBytes.length];
-            System.arraycopy(_lengthBytes, 0, result, 0, _lengthBytes.length);
-            return result;
-        }
-
-        /**
-         * @return A byte array with the V values for this table.
-         */
-        @NotNull
-        public byte[] getValueBytes() {
-            byte[] result = new byte[_valueBytes.length];
-            System.arraycopy(_valueBytes, 0, result, 0, _valueBytes.length);
-            return result;
-        }
-
-        /**
-         * Evaluates whether this table is a "typical" Huffman table.
-         * <p>
-         * "Typical" has a special meaning in this context as the JPEG standard
-         * (ISO/IEC 10918 or ITU-T T.81) defines 4 Huffman tables that has been
-         * developed from the average statistics of a large set of images with
-         * 8-bit precision. Using these instead of calculating the optimal
-         * Huffman tables for a given image is faster, and is preferred by many
-         * hardware encoders and some hardware decoders.
-         * <p>
-         * Even though the JPEG standard doesn't define these as
-         * "standard tables" and requires a decoder to be able to read any valid
-         * Huffman tables, some are in reality limited decoding images using
-         * these "typical" tables. Standards like DCF (Design rule for Camera
-         * File system) and DLNA (Digital Living Network Alliance) actually
-         * requires any compliant JPEG to use only the "typical" Huffman tables.
-         * <p>
-         * This is also related to the term "optimized" JPEG. An "optimized"
-         * JPEG is a JPEG that doesn't use the "typical" Huffman tables.
-         *
-         * @return Whether or not this table is one of the predefined "typical"
-         *         Huffman tables.
-         */
-        public boolean isTypical() {
-            if (_tableClass == HuffmanTableClass.DC) {
-                return
-                    Arrays.equals(_lengthBytes, TYPICAL_LUMINANCE_DC_LENGTHS) &&
-                    Arrays.equals(_valueBytes, TYPICAL_LUMINANCE_DC_VALUES) ||
-                    Arrays.equals(_lengthBytes, TYPICAL_CHROMINANCE_DC_LENGTHS) &&
-                    Arrays.equals(_valueBytes, TYPICAL_CHROMINANCE_DC_VALUES);
-            } else if (_tableClass == HuffmanTableClass.AC) {
-                return
-                    Arrays.equals(_lengthBytes, TYPICAL_LUMINANCE_AC_LENGTHS) &&
-                    Arrays.equals(_valueBytes, TYPICAL_LUMINANCE_AC_VALUES) ||
-                    Arrays.equals(_lengthBytes, TYPICAL_CHROMINANCE_AC_LENGTHS) &&
-                    Arrays.equals(_valueBytes, TYPICAL_CHROMINANCE_AC_VALUES);
-            }
-            return false;
-        }
-
-        /**
-         * The opposite of {@link #isTypical()}.
-         *
-         * @return Whether or not this table is "optimized" - which means that
-         *         it isn't one of the "typical" Huffman tables.
-         */
-        public boolean isOptimized() {
-            return !isTypical();
-        }
-
-        public enum HuffmanTableClass {
-            DC,
-            AC,
-            UNKNOWN;
-
-            public static HuffmanTableClass typeOf(int value) {
-                switch (value) {
-                    case 0:
-                        return DC;
-                    case 1:
-                        return AC;
-                    default:
-                        return UNKNOWN;
-                }
-            }
-        }
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegCommentDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegCommentDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,45 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-/**
- * Provides human-readable string representations of tag values stored in a {@link JpegCommentDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class JpegCommentDescriptor extends TagDescriptor<JpegCommentDirectory>
-{
-    public JpegCommentDescriptor(@NotNull JpegCommentDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Nullable
-    public String getJpegCommentDescription()
-    {
-        return _directory.getString(JpegCommentDirectory.TAG_COMMENT);
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegCommentDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegCommentDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Describes tags used by a JPEG file comment.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class JpegCommentDirectory extends Directory
-{
-    /**
-     * This value does not apply to a particular standard. Rather, this value has been fabricated to maintain
-     * consistency with other directory types.
-     */
-    public static final int TAG_COMMENT = 0;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_COMMENT, "JPEG Comment");
-    }
-
-    public JpegCommentDirectory()
-    {
-        this.setDescriptor(new JpegCommentDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "JpegComment";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegCommentReader.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegCommentReader.java	(revision 16024)
+++ 	(revision )
@@ -1,55 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.StringValue;
-
-import java.util.Collections;
-
-/**
- * Decodes the comment stored within JPEG files, populating a {@link Metadata} object with tag values in a
- * {@link JpegCommentDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class JpegCommentReader implements JpegSegmentMetadataReader
-{
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.COM);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        for (byte[] segmentBytes : segments) {
-            JpegCommentDirectory directory = new JpegCommentDirectory();
-            metadata.addDirectory(directory);
-
-            // The entire contents of the directory are the comment
-            directory.setStringValue(JpegCommentDirectory.TAG_COMMENT, new StringValue(segmentBytes, null));
-        }
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegComponent.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegComponent.java	(revision 16024)
+++ 	(revision )
@@ -1,102 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-
-import java.io.Serializable;
-
-/**
- * Stores information about a JPEG image component such as the component id, horiz/vert sampling factor and
- * quantization table number.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class JpegComponent implements Serializable
-{
-    private static final long serialVersionUID = 61121257899091914L;
-
-    private final int _componentId;
-    private final int _samplingFactorByte;
-    private final int _quantizationTableNumber;
-
-    public JpegComponent(int componentId, int samplingFactorByte, int quantizationTableNumber)
-    {
-        _componentId = componentId;
-        _samplingFactorByte = samplingFactorByte;
-        _quantizationTableNumber = quantizationTableNumber;
-    }
-
-    public int getComponentId()
-    {
-        return _componentId;
-    }
-
-    /**
-     * Returns the component name (one of: Y, Cb, Cr, I, or Q)
-     * @return the component name
-     */
-    @NotNull
-    public String getComponentName()
-    {
-        switch (_componentId)
-        {
-            case 1:
-                return "Y";
-            case 2:
-                return "Cb";
-            case 3:
-                return "Cr";
-            case 4:
-                return "I";
-            case 5:
-                return "Q";
-            default:
-                return String.format("Unknown (%s)", _componentId);
-        }
-    }
-
-    public int getQuantizationTableNumber()
-    {
-        return _quantizationTableNumber;
-    }
-
-    public int getHorizontalSamplingFactor()
-    {
-        return (_samplingFactorByte>>4) & 0x0F;
-    }
-
-    public int getVerticalSamplingFactor()
-    {
-        return _samplingFactorByte & 0x0F;
-    }
-
-    @NotNull
-    @Override
-    public String toString() {
-        return String.format(
-            "Quantization table %d, Sampling factors %d horiz/%d vert",
-            _quantizationTableNumber,
-            getHorizontalSamplingFactor(),
-            getVerticalSamplingFactor()
-        );
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,129 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.jpeg.JpegDirectory.*;
-
-/**
- * Provides human-readable string versions of the tags stored in a JpegDirectory.
- * Thanks to Darrell Silver (www.darrellsilver.com) for the initial version of this class.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class JpegDescriptor extends TagDescriptor<JpegDirectory>
-{
-    public JpegDescriptor(@NotNull JpegDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    @Nullable
-    public String getDescription(int tagType)
-    {
-        switch (tagType)
-        {
-            case TAG_COMPRESSION_TYPE:
-                return getImageCompressionTypeDescription();
-            case TAG_COMPONENT_DATA_1:
-                return getComponentDataDescription(0);
-            case TAG_COMPONENT_DATA_2:
-                return getComponentDataDescription(1);
-            case TAG_COMPONENT_DATA_3:
-                return getComponentDataDescription(2);
-            case TAG_COMPONENT_DATA_4:
-                return getComponentDataDescription(3);
-            case TAG_DATA_PRECISION:
-                return getDataPrecisionDescription();
-            case TAG_IMAGE_HEIGHT:
-                return getImageHeightDescription();
-            case TAG_IMAGE_WIDTH:
-                return getImageWidthDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getImageCompressionTypeDescription()
-    {
-        return getIndexedDescription(TAG_COMPRESSION_TYPE,
-            "Baseline",
-            "Extended sequential, Huffman",
-            "Progressive, Huffman",
-            "Lossless, Huffman",
-            null, // no 4
-            "Differential sequential, Huffman",
-            "Differential progressive, Huffman",
-            "Differential lossless, Huffman",
-            "Reserved for JPEG extensions",
-            "Extended sequential, arithmetic",
-            "Progressive, arithmetic",
-            "Lossless, arithmetic",
-            null, // no 12
-            "Differential sequential, arithmetic",
-            "Differential progressive, arithmetic",
-            "Differential lossless, arithmetic");
-    }
-
-    @Nullable
-    public String getImageWidthDescription()
-    {
-        final String value = _directory.getString(TAG_IMAGE_WIDTH);
-        if (value==null)
-            return null;
-        return value + " pixels";
-    }
-
-    @Nullable
-    public String getImageHeightDescription()
-    {
-        final String value = _directory.getString(TAG_IMAGE_HEIGHT);
-        if (value==null)
-            return null;
-        return value + " pixels";
-    }
-
-    @Nullable
-    public String getDataPrecisionDescription()
-    {
-        final String value = _directory.getString(TAG_DATA_PRECISION);
-        if (value==null)
-            return null;
-        return value + " bits";
-    }
-
-    @Nullable
-    public String getComponentDataDescription(int componentNumber)
-    {
-        JpegComponent value = _directory.getComponent(componentNumber);
-
-        if (value==null)
-            return null;
-
-        return value.getComponentName() + " component: " + value;
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegDhtReader.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegDhtReader.java	(revision 16024)
+++ 	(revision )
@@ -1,101 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.jpeg.HuffmanTablesDirectory.HuffmanTable;
-import com.drew.metadata.jpeg.HuffmanTablesDirectory.HuffmanTable.HuffmanTableClass;
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * Reader for JPEG Huffman tables, found in the DHT JPEG segment.
- *
- * @author Nadahar
- */
-public class JpegDhtReader implements JpegSegmentMetadataReader
-{
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.DHT);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        for (byte[] segmentBytes : segments) {
-            extract(new SequentialByteArrayReader(segmentBytes), metadata);
-        }
-    }
-
-    /**
-     * Performs the DHT tables extraction, adding found tables to the specified
-     * instance of {@link Metadata}.
-     */
-    public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata)
-    {
-        HuffmanTablesDirectory directory = metadata.getFirstDirectoryOfType(HuffmanTablesDirectory.class);
-        if (directory == null) {
-            directory = new HuffmanTablesDirectory();
-            metadata.addDirectory(directory);
-        }
-
-        try {
-            while (reader.available() > 0) {
-                byte header = reader.getByte();
-                HuffmanTableClass tableClass = HuffmanTableClass.typeOf((header & 0xF0) >> 4);
-                int tableDestinationId = header & 0xF;
-
-                byte[] lBytes = getBytes(reader, 16);
-                int vCount = 0;
-                for (byte b : lBytes) {
-                    vCount += (b & 0xFF);
-                }
-                byte[] vBytes = getBytes(reader, vCount);
-                directory.getTables().add(new HuffmanTable(tableClass, tableDestinationId, lBytes, vBytes));
-            }
-        } catch (IOException me) {
-            directory.addError(me.getMessage());
-        }
-
-        directory.setInt(HuffmanTablesDirectory.TAG_NUMBER_OF_TABLES, directory.getTables().size());
-    }
-
-    private byte[] getBytes(@NotNull final SequentialReader reader, int count) throws IOException {
-        byte[] bytes = new byte[count];
-        for (int i = 0; i < count; i++) {
-            byte b = reader.getByte();
-            if ((b & 0xFF) == 0xFF) {
-                byte stuffing = reader.getByte();
-                if (stuffing != 0x00) {
-                    throw new IOException("Marker " + JpegSegmentType.fromByte(stuffing) + " found inside DHT segment");
-                }
-            }
-            bytes[i] = b;
-        }
-        return bytes;
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,127 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.MetadataException;
-
-import java.util.HashMap;
-
-/**
- * Directory of tags and values for the SOF0 JPEG segment.  This segment holds basic metadata about the image.
- *
- * @author Darrell Silver http://www.darrellsilver.com and Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class JpegDirectory extends Directory
-{
-    public static final int TAG_COMPRESSION_TYPE = -3;
-    /** This is in bits/sample, usually 8 (12 and 16 not supported by most software). */
-    public static final int TAG_DATA_PRECISION = 0;
-    /** The image's height.  Necessary for decoding the image, so it should always be there. */
-    public static final int TAG_IMAGE_HEIGHT = 1;
-    /** The image's width.  Necessary for decoding the image, so it should always be there. */
-    public static final int TAG_IMAGE_WIDTH = 3;
-    /**
-     * Usually 1 = grey scaled, 3 = color YcbCr or YIQ, 4 = color CMYK
-     * Each component TAG_COMPONENT_DATA_[1-4], has the following meaning:
-     * component Id(1byte)(1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q),
-     * sampling factors (1byte) (bit 0-3 vertical., 4-7 horizontal.),
-     * quantization table number (1 byte).
-     * <p>
-     * This info is from http://www.funducode.com/freec/Fileformats/format3/format3b.htm
-     */
-    public static final int TAG_NUMBER_OF_COMPONENTS = 5;
-
-    // NOTE!  Component tag type int values must increment in steps of 1
-
-    /** the first of a possible 4 color components.  Number of components specified in TAG_NUMBER_OF_COMPONENTS. */
-    public static final int TAG_COMPONENT_DATA_1 = 6;
-    /** the second of a possible 4 color components.  Number of components specified in TAG_NUMBER_OF_COMPONENTS. */
-    public static final int TAG_COMPONENT_DATA_2 = 7;
-    /** the third of a possible 4 color components.  Number of components specified in TAG_NUMBER_OF_COMPONENTS. */
-    public static final int TAG_COMPONENT_DATA_3 = 8;
-    /** the fourth of a possible 4 color components.  Number of components specified in TAG_NUMBER_OF_COMPONENTS. */
-    public static final int TAG_COMPONENT_DATA_4 = 9;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_COMPRESSION_TYPE, "Compression Type");
-        _tagNameMap.put(TAG_DATA_PRECISION, "Data Precision");
-        _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
-        _tagNameMap.put(TAG_IMAGE_HEIGHT, "Image Height");
-        _tagNameMap.put(TAG_NUMBER_OF_COMPONENTS, "Number of Components");
-        _tagNameMap.put(TAG_COMPONENT_DATA_1, "Component 1");
-        _tagNameMap.put(TAG_COMPONENT_DATA_2, "Component 2");
-        _tagNameMap.put(TAG_COMPONENT_DATA_3, "Component 3");
-        _tagNameMap.put(TAG_COMPONENT_DATA_4, "Component 4");
-    }
-
-    public JpegDirectory()
-    {
-        this.setDescriptor(new JpegDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "JPEG";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    /**
-     * @param componentNumber The zero-based index of the component.  This number is normally between 0 and 3.
-     *                        Use getNumberOfComponents for bounds-checking.
-     * @return the JpegComponent having the specified number.
-     */
-    @Nullable
-    public JpegComponent getComponent(int componentNumber)
-    {
-        int tagType = JpegDirectory.TAG_COMPONENT_DATA_1 + componentNumber;
-        return (JpegComponent)getObject(tagType);
-    }
-
-    public int getImageWidth() throws MetadataException
-    {
-        return getInt(JpegDirectory.TAG_IMAGE_WIDTH);
-    }
-
-    public int getImageHeight() throws MetadataException
-    {
-        return getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
-    }
-
-    public int getNumberOfComponents() throws MetadataException
-    {
-        return getInt(JpegDirectory.TAG_NUMBER_OF_COMPONENTS);
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegDnlReader.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegDnlReader.java	(revision 16024)
+++ 	(revision )
@@ -1,76 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.ErrorDirectory;
-import com.drew.metadata.Metadata;
-
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * Decodes JPEG DNL data, adjusting the image height with information missing from the JPEG SOFx segment.
- *
- * @author Nadahar
- */
-public class JpegDnlReader implements JpegSegmentMetadataReader
-{
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.DNL);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        for (byte[] segmentBytes : segments) {
-            extract(segmentBytes, metadata, segmentType);
-        }
-    }
-
-    public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType)
-    {
-        JpegDirectory directory = metadata.getFirstDirectoryOfType(JpegDirectory.class);
-        if (directory == null) {
-            ErrorDirectory errorDirectory = new ErrorDirectory();
-            metadata.addDirectory(errorDirectory);
-            errorDirectory.addError("DNL segment found without SOFx - illegal JPEG format");
-            return;
-        }
-
-        SequentialReader reader = new SequentialByteArrayReader(segmentBytes);
-
-        try {
-            // Only set height from DNL if it's not already defined
-            Integer i = directory.getInteger(JpegDirectory.TAG_IMAGE_HEIGHT);
-            if (i == null || i == 0) {
-                directory.setInt(JpegDirectory.TAG_IMAGE_HEIGHT, reader.getUInt16());
-            }
-        } catch (IOException ex) {
-            directory.addError(ex.getMessage());
-        }
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/JpegReader.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/JpegReader.java	(revision 16024)
+++ 	(revision )
@@ -1,104 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.jpeg;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-/**
- * Decodes JPEG SOFn data, populating a {@link Metadata} object with tag values in a {@link JpegDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- * @author Darrell Silver http://www.darrellsilver.com
- */
-public class JpegReader implements JpegSegmentMetadataReader
-{
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        // NOTE that some SOFn values do not exist
-        return Arrays.asList(
-            JpegSegmentType.SOF0,
-            JpegSegmentType.SOF1,
-            JpegSegmentType.SOF2,
-            JpegSegmentType.SOF3,
-//            JpegSegmentType.SOF4,
-            JpegSegmentType.SOF5,
-            JpegSegmentType.SOF6,
-            JpegSegmentType.SOF7,
-//            JpegSegmentType.JPG,
-            JpegSegmentType.SOF9,
-            JpegSegmentType.SOF10,
-            JpegSegmentType.SOF11,
-//            JpegSegmentType.SOF12,
-            JpegSegmentType.SOF13,
-            JpegSegmentType.SOF14,
-            JpegSegmentType.SOF15
-        );
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        for (byte[] segmentBytes : segments) {
-            extract(segmentBytes, metadata, segmentType);
-        }
-    }
-
-    public void extract(byte[] segmentBytes, Metadata metadata, JpegSegmentType segmentType)
-    {
-        JpegDirectory directory = new JpegDirectory();
-        metadata.addDirectory(directory);
-
-        // The value of TAG_COMPRESSION_TYPE is determined by the segment type found
-        directory.setInt(JpegDirectory.TAG_COMPRESSION_TYPE, segmentType.byteValue - JpegSegmentType.SOF0.byteValue);
-
-        SequentialReader reader = new SequentialByteArrayReader(segmentBytes);
-
-        try {
-            directory.setInt(JpegDirectory.TAG_DATA_PRECISION, reader.getUInt8());
-            directory.setInt(JpegDirectory.TAG_IMAGE_HEIGHT, reader.getUInt16());
-            directory.setInt(JpegDirectory.TAG_IMAGE_WIDTH, reader.getUInt16());
-            short componentCount = reader.getUInt8();
-            directory.setInt(JpegDirectory.TAG_NUMBER_OF_COMPONENTS, componentCount);
-
-            // for each component, there are three bytes of data:
-            // 1 - Component ID: 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
-            // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
-            // 3 - Quantization table number
-            for (int i = 0; i < (int)componentCount; i++) {
-                final int componentId = reader.getUInt8();
-                final int samplingFactorByte = reader.getUInt8();
-                final int quantizationTableNumber = reader.getUInt8();
-                final JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
-                directory.setObject(JpegDirectory.TAG_COMPONENT_DATA_1 + i, component);
-            }
-        } catch (IOException ex) {
-            directory.addError(ex.getMessage());
-        }
-    }
-}
Index: unk/src/com/drew/metadata/jpeg/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/jpeg/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of JPEG file format metadata.
- */
-package com.drew.metadata.jpeg;
Index: unk/src/com/drew/metadata/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,6 +1,0 @@
-/**
- * Provides classes for generic modelling of metadata directories and tags.
- * <p />
- * Contains base types for metadata processing abstraction.
- */
-package com.drew.metadata;
Index: unk/src/com/drew/metadata/photoshop/DuckyDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/DuckyDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,69 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.photoshop;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-import com.drew.metadata.TagDescriptor;
-
-import java.util.HashMap;
-
-/**
- * Holds the data found in Photoshop "ducky" segments, created during Save-for-Web.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class DuckyDirectory extends Directory
-{
-    public static final int TAG_QUALITY = 1;
-    public static final int TAG_COMMENT = 2;
-    public static final int TAG_COPYRIGHT = 3;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_QUALITY, "Quality");
-        _tagNameMap.put(TAG_COMMENT, "Comment");
-        _tagNameMap.put(TAG_COPYRIGHT, "Copyright");
-    }
-
-    public DuckyDirectory()
-    {
-        this.setDescriptor(new TagDescriptor<DuckyDirectory>(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Ducky";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/DuckyReader.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/DuckyReader.java	(revision 16024)
+++ 	(revision )
@@ -1,116 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.photoshop;
-
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.Charsets;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * Reads Photoshop "ducky" segments, created during Save-for-Web.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class DuckyReader implements JpegSegmentMetadataReader
-{
-    @NotNull
-    private static final String JPEG_SEGMENT_PREAMBLE = "Ducky";
-
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.APPC);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        final int preambleLength = JPEG_SEGMENT_PREAMBLE.length();
-
-        for (byte[] segmentBytes : segments) {
-            // Ensure data starts with the necessary preamble
-            if (segmentBytes.length < preambleLength || !JPEG_SEGMENT_PREAMBLE.equals(new String(segmentBytes, 0, preambleLength)))
-                continue;
-
-            extract(
-                new SequentialByteArrayReader(segmentBytes, preambleLength),
-                metadata);
-        }
-    }
-
-    public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata)
-    {
-        DuckyDirectory directory = new DuckyDirectory();
-        metadata.addDirectory(directory);
-
-        try
-        {
-            while (true)
-            {
-                int tag = reader.getUInt16();
-
-                // End of Segment is marked with zero
-                if (tag == 0)
-                    break;
-
-                int length = reader.getUInt16();
-
-                switch (tag)
-                {
-                    case DuckyDirectory.TAG_QUALITY:
-                    {
-                        if (length != 4)
-                        {
-                            directory.addError("Unexpected length for the quality tag");
-                            return;
-                        }
-                        directory.setInt(tag, reader.getInt32());
-                        break;
-                    }
-                    case DuckyDirectory.TAG_COMMENT:
-                    case DuckyDirectory.TAG_COPYRIGHT:
-                    {
-                        reader.skip(4);
-                        directory.setStringValue(tag, reader.getStringValue(length - 4, Charsets.UTF_16BE));
-                        break;
-                    }
-                    default:
-                    {
-                        // Unexpected tag
-                        directory.setByteArray(tag, reader.getBytes(length));
-                        break;
-                    }
-                }
-            }
-        }
-        catch (IOException e)
-        {
-            directory.addError(e.getMessage());
-        }
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/Knot.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/Knot.java	(revision 16024)
+++ 	(revision )
@@ -1,55 +1,0 @@
-package com.drew.metadata.photoshop;
-
-
-/**
- * Represents a knot created by Photoshop:
- *
- * <ul>
- *   <li>Linked knot</li>
- *   <li>Unlinked knot</li>
- * </ul>
- *
- * @author Payton Garland
- */
-public class Knot
-{
-    private final double[] _points = new double[6];
-    private final String _type;
-
-    public Knot(String type)
-    {
-        _type = type;
-    }
-
-    /**
-     * Add an individual coordinate value (x or y) to
-     * points array (6 points per knot)
-     *
-     * @param index location of point to be added in points
-     * @param point coordinate value to be added to points
-     */
-    public void setPoint(int index, double point)
-    {
-        _points[index] = point;
-    }
-
-    /**
-     * Get an individual coordinate value (x or y)
-     *
-     * @return an individual coordinate value
-     */
-    public double getPoint(int index)
-    {
-        return _points[index];
-    }
-
-    /**
-     * Get the type of knot (linked or unlinked)
-     *
-     * @return the type of knot
-     */
-    public String getType()
-    {
-        return this._type;
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PhotoshopDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PhotoshopDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,488 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.photoshop;
-
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.Charsets;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-
-import static com.drew.metadata.photoshop.PhotoshopDirectory.*;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- * @author Yuri Binev
- * @author Payton Garland
- */
-@SuppressWarnings("WeakerAccess")
-public class PhotoshopDescriptor extends TagDescriptor<PhotoshopDirectory>
-{
-    public PhotoshopDescriptor(@NotNull PhotoshopDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_THUMBNAIL:
-            case TAG_THUMBNAIL_OLD:
-                return getThumbnailDescription(tagType);
-            case TAG_URL:
-            case TAG_XML:
-                return getSimpleString(tagType);
-            case TAG_IPTC:
-                return getBinaryDataString(tagType);
-            case TAG_SLICES:
-                return getSlicesDescription();
-            case TAG_VERSION:
-                return getVersionDescription();
-            case TAG_COPYRIGHT:
-                return getBooleanString(tagType);
-            case TAG_RESOLUTION_INFO:
-                return getResolutionInfoDescription();
-            case TAG_GLOBAL_ANGLE:
-            case TAG_GLOBAL_ALTITUDE:
-            case TAG_URL_LIST:
-            case TAG_SEED_NUMBER:
-                return get32BitNumberString(tagType);
-            case TAG_JPEG_QUALITY:
-                return getJpegQualityString();
-            case TAG_PRINT_SCALE:
-                return getPrintScaleDescription();
-            case TAG_PIXEL_ASPECT_RATIO:
-                return getPixelAspectRatioString();
-            case TAG_CLIPPING_PATH_NAME:
-                return getClippingPathNameString(tagType);
-            default:
-                if (tagType >= 0x07D0 && tagType <= 0x0BB6)
-                    return getPathString(tagType);
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getJpegQualityString()
-    {
-        try {
-            byte[] b = _directory.getByteArray(TAG_JPEG_QUALITY);
-
-            if (b == null)
-                return _directory.getString(TAG_JPEG_QUALITY);
-
-            RandomAccessReader reader = new ByteArrayReader(b);
-            int q = reader.getUInt16(0); // & 0xFFFF;
-            int f = reader.getUInt16(2); // & 0xFFFF;
-            int s = reader.getUInt16(4);
-
-            int q1 = q <= 0xFFFF && q >= 0xFFFD
-                ? q - 0xFFFC
-                : q <= 8
-                    ? q + 4
-                    : q;
-
-            String quality;
-            switch (q) {
-                case 0xFFFD:
-                case 0xFFFE:
-                case 0xFFFF:
-                case 0:
-                    quality = "Low";
-                    break;
-                case 1:
-                case 2:
-                case 3:
-                    quality = "Medium";
-                    break;
-                case 4:
-                case 5:
-                    quality = "High";
-                    break;
-                case 6:
-                case 7:
-                case 8:
-                    quality = "Maximum";
-                    break;
-                default:
-                    quality = "Unknown";
-            }
-
-            String format;
-            switch (f) {
-                case 0x0000:
-                    format = "Standard";
-                    break;
-                case 0x0001:
-                    format = "Optimised";
-                    break;
-                case 0x0101:
-                    format = "Progressive";
-                    break;
-                default:
-                    format = String.format("Unknown 0x%04X", f);
-            }
-
-            String scans = s >= 1 && s <= 3
-                    ? String.format("%d", s + 2)
-                    : String.format("Unknown 0x%04X", s);
-
-            return String.format("%d (%s), %s format, %s scans", q1, quality, format, scans);
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getPixelAspectRatioString()
-    {
-        try {
-            byte[] bytes = _directory.getByteArray(TAG_PIXEL_ASPECT_RATIO);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            double d = reader.getDouble64(4);
-            return Double.toString(d);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getPrintScaleDescription()
-    {
-        try {
-            byte bytes[] = _directory.getByteArray(TAG_PRINT_SCALE);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            int style = reader.getInt32(0);
-            float locX = reader.getFloat32(2);
-            float locY = reader.getFloat32(6);
-            float scale = reader.getFloat32(10);
-            switch (style) {
-                case 0:
-                    return "Centered, Scale " + scale;
-                case 1:
-                    return "Size to fit";
-                case 2:
-                    return String.format("User defined, X:%s Y:%s, Scale:%s", locX, locY, scale);
-                default:
-                    return String.format("Unknown %04X, X:%s Y:%s, Scale:%s", style, locX, locY, scale);
-            }
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getResolutionInfoDescription()
-    {
-        try {
-            byte[] bytes = _directory.getByteArray(TAG_RESOLUTION_INFO);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            float resX = reader.getS15Fixed16(0);
-            float resY = reader.getS15Fixed16(8); // is this the correct offset? it's only reading 4 bytes each time
-            DecimalFormat format = new DecimalFormat("0.##");
-            return format.format(resX) + "x" + format.format(resY) + " DPI";
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getVersionDescription()
-    {
-        try {
-            final byte[] bytes = _directory.getByteArray(TAG_VERSION);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            int pos = 0;
-            int ver = reader.getInt32(0);
-            pos += 4;
-            pos++;
-            int readerLength = reader.getInt32(5);
-            pos += 4;
-            String readerStr = reader.getString(9, readerLength * 2, "UTF-16");
-            pos += readerLength * 2;
-            int writerLength = reader.getInt32(pos);
-            pos += 4;
-            String writerStr = reader.getString(pos, writerLength * 2, "UTF-16");
-            pos += writerLength * 2;
-            int fileVersion = reader.getInt32(pos);
-            return String.format("%d (%s, %s) %d", ver, readerStr, writerStr, fileVersion);
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getSlicesDescription()
-    {
-        try {
-            final byte bytes[] = _directory.getByteArray(TAG_SLICES);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            int nameLength = reader.getInt32(20);
-            String name = reader.getString(24, nameLength * 2, "UTF-16");
-            int pos = 24 + nameLength * 2;
-            int sliceCount = reader.getInt32(pos);
-            return String.format("%s (%d,%d,%d,%d) %d Slices",
-                    name, reader.getInt32(4), reader.getInt32(8), reader.getInt32(12), reader.getInt32(16), sliceCount);
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getThumbnailDescription(int tagType)
-    {
-        try {
-            byte[] v = _directory.getByteArray(tagType);
-            if (v == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(v);
-            int format = reader.getInt32(0);
-            int width = reader.getInt32(4);
-            int height = reader.getInt32(8);
-            //skip WidthBytes
-            int totalSize = reader.getInt32(16);
-            int compSize = reader.getInt32(20);
-            int bpp = reader.getInt32(24);
-            //skip Number of planes
-            return String.format("%s, %dx%d, Decomp %d bytes, %d bpp, %d bytes",
-                    format == 1 ? "JpegRGB" : "RawRGB",
-                    width, height, totalSize, bpp, compSize);
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    private String getBooleanString(int tag)
-    {
-        final byte[] bytes = _directory.getByteArray(tag);
-        if (bytes == null || bytes.length == 0)
-            return null;
-        return bytes[0] == 0 ? "No" : "Yes";
-    }
-
-    @Nullable
-    private String get32BitNumberString(int tag)
-    {
-        byte[] bytes = _directory.getByteArray(tag);
-        if (bytes == null)
-            return null;
-        RandomAccessReader reader = new ByteArrayReader(bytes);
-        try {
-            return String.format("%d", reader.getInt32(0));
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    private String getSimpleString(int tagType)
-    {
-        final byte[] bytes = _directory.getByteArray(tagType);
-        if (bytes == null)
-            return null;
-        return new String(bytes);
-    }
-
-    @Nullable
-    private String getBinaryDataString(int tagType)
-    {
-        final byte[] bytes = _directory.getByteArray(tagType);
-        if (bytes == null)
-            return null;
-        return String.format("%d bytes binary data", bytes.length);
-    }
-
-    @Nullable
-    public String getClippingPathNameString(int tagType)
-    {
-        try {
-            byte[] bytes = _directory.getByteArray(tagType);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            int length = reader.getByte(0);
-            return new String(reader.getBytes(1, length), "UTF-8");
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    @Nullable
-    public String getPathString(int tagType)
-    {
-        try {
-            byte[] bytes = _directory.getByteArray(tagType);
-            if (bytes == null)
-                return null;
-            RandomAccessReader reader = new ByteArrayReader(bytes);
-            int length = (int) (reader.getLength() - reader.getByte((int)reader.getLength() - 1) - 1) / 26;
-
-            String fillRecord = null;
-
-            // Possible subpaths
-            Subpath cSubpath = new Subpath();
-            Subpath oSubpath = new Subpath();
-
-            ArrayList<Subpath> paths = new ArrayList<Subpath>();
-
-            // Loop through each path resource block segment (26-bytes)
-            for (int i = 0; i < length; i++) {
-                // Spacer takes into account which block is currently being worked on while accessing byte array
-                int recordSpacer = 26 * i;
-                int selector = reader.getInt16(recordSpacer);
-
-                /*
-                 * Subpath resource blocks come in 26-byte segments with 9 possible selectors - some selectors
-                 * are formatted different from others
-                 *
-                 *      0 = Closed subpath length record
-                 *      1 = Closed subpath Bezier knot, linked
-                 *      2 = Closed subpath Bezier knot, unlinked
-                 *      3 = Open subpath length record
-                 *      4 = Open subpath Bezier knot, linked
-                 *      5 = Open subpath Bezier knot, unlinked
-                 *      6 = Subpath fill rule record
-                 *      7 = Clipboard record
-                 *      8 = Initial fill rule record
-                 *
-                 * Source: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
-                 */
-                switch (selector) {
-                    case 0:
-                        // Insert previous Paths if there are any
-                        if (cSubpath.size() != 0) {
-                            paths.add(cSubpath);
-                        }
-
-                        // Make path size accordingly
-                        cSubpath = new Subpath("Closed Subpath");
-                        break;
-                    case 1:
-                    case 2:
-                    {
-                        Knot knot;
-                        if (selector == 1)
-                            knot = new Knot("Linked");
-                        else
-                            knot = new Knot("Unlinked");
-                        // Insert each point into cSubpath - points are 32-bit signed, fixed point numbers and have 8-bits before the point
-                        for (int j = 0; j < 6; j++) {
-                            knot.setPoint(j, reader.getInt8((j * 4) + 2 + recordSpacer) + (reader.getInt24((j * 4) + 3 + recordSpacer) / Math.pow(2.0, 24.0)));
-                        }
-                        cSubpath.add(knot);
-                        break;
-                    }
-                    case 3:
-                        // Insert previous Paths if there are any
-                        if (oSubpath.size() != 0) {
-                            paths.add(oSubpath);
-                        }
-
-                        // Make path size accordingly
-                        oSubpath = new Subpath("Open Subpath");
-                        break;
-                    case 4:
-                    case 5:
-                    {
-                        Knot knot;
-                        if (selector == 4)
-                            knot = new Knot("Linked");
-                        else
-                            knot = new Knot("Unlinked");
-                        // Insert each point into oSubpath - points are 32-bit signed, fixed point numbers and have 8-bits before the point
-                        for (int j = 0; j < 6; j++) {
-                            knot.setPoint(j, reader.getInt8((j * 4) + 2 + recordSpacer) + (reader.getInt24((j * 4) + 3 + recordSpacer) / Math.pow(2.0, 24.0)));
-                        }
-                        oSubpath.add(knot);
-                        break;
-                    }
-                    case 6:
-                        break;
-                    case 7:
-                        // TODO: Clipboard record
-//                        for (int j = 0; j < 24; j++) {
-//                           clipboardRecord[j] = bytes[j + 2 + recordSpacer];
-//                        }
-                        break;
-                    case 8:
-                        if (reader.getInt16(2 + recordSpacer) == 1)
-                            fillRecord = "with all pixels";
-                        else
-                            fillRecord = "without all pixels";
-                        break;
-                }
-            }
-
-            // Add any more paths that were not added already
-            if (cSubpath.size() != 0)
-                paths.add(cSubpath);
-            if (oSubpath.size() != 0)
-                paths.add(oSubpath);
-
-            // Extract name (previously appended to end of byte array)
-            int nameLength = reader.getByte((int)reader.getLength() - 1);
-            String name = reader.getString((int)reader.getLength() - nameLength - 1, nameLength, Charsets.ASCII);
-
-            // Build description
-            StringBuilder str = new StringBuilder();
-
-            str.append('"').append(name).append('"')
-                .append(" having ");
-
-            if (fillRecord != null)
-                str.append("initial fill rule \"").append(fillRecord).append("\" and ");
-
-            str.append(paths.size()).append(paths.size() == 1 ? " subpath:" : " subpaths:");
-
-            for (Subpath path : paths) {
-                str.append("\n- ").append(path.getType()).append(" with ").append(paths.size()).append(paths.size() == 1 ? " knot:" : " knots:");
-
-                for (Knot knot : path.getKnots()) {
-                    str.append("\n  - ").append(knot.getType());
-                    str.append(" (").append(knot.getPoint(0)).append(",").append(knot.getPoint(1)).append(")");
-                    str.append(" (").append(knot.getPoint(2)).append(",").append(knot.getPoint(3)).append(")");
-                    str.append(" (").append(knot.getPoint(4)).append(",").append(knot.getPoint(5)).append(")");
-                }
-            }
-
-            return str.toString();
-        } catch (Exception e) {
-            return null;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PhotoshopDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PhotoshopDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,260 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.photoshop;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Holds the metadata found in the APPD segment of a JPEG file saved by Photoshop.
- *
- * @author Drew Noakes https://drewnoakes.com
- * @author Yuri Binev
- * @author Payton Garland
- */
-@SuppressWarnings("WeakerAccess")
-public class PhotoshopDirectory extends Directory
-{
-    public static final int TAG_CHANNELS_ROWS_COLUMNS_DEPTH_MODE                  = 0x03E8;
-    public static final int TAG_MAC_PRINT_INFO                                    = 0x03E9;
-    public static final int TAG_XML                                               = 0x03EA;
-    public static final int TAG_INDEXED_COLOR_TABLE                               = 0x03EB;
-    public static final int TAG_RESOLUTION_INFO                                   = 0x03ED;
-    public static final int TAG_ALPHA_CHANNELS                                    = 0x03EE;
-    public static final int TAG_DISPLAY_INFO_OBSOLETE                             = 0x03EF;
-    public static final int TAG_CAPTION                                           = 0x03F0;
-    public static final int TAG_BORDER_INFORMATION                                = 0x03F1;
-    public static final int TAG_BACKGROUND_COLOR                                  = 0x03F2;
-    public static final int TAG_PRINT_FLAGS                                       = 0x03F3;
-    public static final int TAG_GRAYSCALE_AND_MULTICHANNEL_HALFTONING_INFORMATION = 0x03F4;
-    public static final int TAG_COLOR_HALFTONING_INFORMATION                      = 0x03F5;
-    public static final int TAG_DUOTONE_HALFTONING_INFORMATION                    = 0x03F6;
-    public static final int TAG_GRAYSCALE_AND_MULTICHANNEL_TRANSFER_FUNCTION      = 0x03F7;
-    public static final int TAG_COLOR_TRANSFER_FUNCTIONS                          = 0x03F8;
-    public static final int TAG_DUOTONE_TRANSFER_FUNCTIONS                        = 0x03F9;
-    public static final int TAG_DUOTONE_IMAGE_INFORMATION                         = 0x03FA;
-    public static final int TAG_EFFECTIVE_BLACK_AND_WHITE_VALUES                  = 0x03FB;
-    // OBSOLETE                                                                     0x03FC
-    public static final int TAG_EPS_OPTIONS                                       = 0x03FD;
-    public static final int TAG_QUICK_MASK_INFORMATION                            = 0x03FE;
-    // OBSOLETE                                                                     0x03FF
-    public static final int TAG_LAYER_STATE_INFORMATION                           = 0x0400;
-    // Working path (not saved)                                                     0x0401
-    public static final int TAG_LAYERS_GROUP_INFORMATION                          = 0x0402;
-    // OBSOLETE                                                                     0x0403
-    public static final int TAG_IPTC                                              = 0x0404;
-    public static final int TAG_IMAGE_MODE_FOR_RAW_FORMAT_FILES                   = 0x0405;
-    public static final int TAG_JPEG_QUALITY                                      = 0x0406;
-    public static final int TAG_GRID_AND_GUIDES_INFORMATION                       = 0x0408;
-    public static final int TAG_THUMBNAIL_OLD                                     = 0x0409;
-    public static final int TAG_COPYRIGHT                                         = 0x040A;
-    public static final int TAG_URL                                               = 0x040B;
-    public static final int TAG_THUMBNAIL                                         = 0x040C;
-    public static final int TAG_GLOBAL_ANGLE                                      = 0x040D;
-    // OBSOLETE                                                                     0x040E
-    public static final int TAG_ICC_PROFILE_BYTES                                 = 0x040F;
-    public static final int TAG_WATERMARK                                         = 0x0410;
-    public static final int TAG_ICC_UNTAGGED_PROFILE                              = 0x0411;
-    public static final int TAG_EFFECTS_VISIBLE                                   = 0x0412;
-    public static final int TAG_SPOT_HALFTONE                                     = 0x0413;
-    public static final int TAG_SEED_NUMBER                                       = 0x0414;
-    public static final int TAG_UNICODE_ALPHA_NAMES                               = 0x0415;
-    public static final int TAG_INDEXED_COLOR_TABLE_COUNT                         = 0x0416;
-    public static final int TAG_TRANSPARENCY_INDEX                                = 0x0417;
-    public static final int TAG_GLOBAL_ALTITUDE                                   = 0x0419;
-    public static final int TAG_SLICES                                            = 0x041A;
-    public static final int TAG_WORKFLOW_URL                                      = 0x041B;
-    public static final int TAG_JUMP_TO_XPEP                                      = 0x041C;
-    public static final int TAG_ALPHA_IDENTIFIERS                                 = 0x041D;
-    public static final int TAG_URL_LIST                                          = 0x041E;
-    public static final int TAG_VERSION                                           = 0x0421;
-    public static final int TAG_EXIF_DATA_1                                       = 0x0422;
-    public static final int TAG_EXIF_DATA_3                                       = 0x0423;
-    public static final int TAG_XMP_DATA                                          = 0x0424;
-    public static final int TAG_CAPTION_DIGEST                                    = 0x0425;
-    public static final int TAG_PRINT_SCALE                                       = 0x0426;
-    public static final int TAG_PIXEL_ASPECT_RATIO                                = 0x0428;
-    public static final int TAG_LAYER_COMPS                                       = 0x0429;
-    public static final int TAG_ALTERNATE_DUOTONE_COLORS                          = 0x042A;
-    public static final int TAG_ALTERNATE_SPOT_COLORS                             = 0x042B;
-    public static final int TAG_LAYER_SELECTION_IDS                               = 0x042D;
-    public static final int TAG_HDR_TONING_INFO                                   = 0x042E;
-    public static final int TAG_PRINT_INFO                                        = 0x042F;
-    public static final int TAG_LAYER_GROUPS_ENABLED_ID                           = 0x0430;
-    public static final int TAG_COLOR_SAMPLERS                                    = 0x0431;
-    public static final int TAG_MEASUREMENT_SCALE                                 = 0x0432;
-    public static final int TAG_TIMELINE_INFORMATION                              = 0x0433;
-    public static final int TAG_SHEET_DISCLOSURE                                  = 0x0434;
-    public static final int TAG_DISPLAY_INFO                                      = 0x0435;
-    public static final int TAG_ONION_SKINS                                       = 0x0436;
-    public static final int TAG_COUNT_INFORMATION                                 = 0x0438;
-    public static final int TAG_PRINT_INFO_2                                      = 0x043A;
-    public static final int TAG_PRINT_STYLE                                       = 0x043B;
-    public static final int TAG_MAC_NSPRINTINFO                                   = 0x043C;
-    public static final int TAG_WIN_DEVMODE                                       = 0x043D;
-    public static final int TAG_AUTO_SAVE_FILE_PATH                               = 0x043E;
-    public static final int TAG_AUTO_SAVE_FORMAT                                  = 0x043F;
-    public static final int TAG_PATH_SELECTION_STATE                              = 0x0440;
-    // PATH INFO                                                                    0x07D0 -> 0x0BB6
-    public static final int TAG_CLIPPING_PATH_NAME                                = 0x0BB7;
-    public static final int TAG_ORIGIN_PATH_INFO                                  = 0x0BB8;
-    // PLUG IN RESOURCES                                                            0x0FA0 -> 0x1387
-    public static final int TAG_IMAGE_READY_VARIABLES_XML                         = 0x1B58;
-    public static final int TAG_IMAGE_READY_DATA_SETS                             = 0x1B59;
-    public static final int TAG_IMAGE_READY_SELECTED_STATE                        = 0x1B5A;
-    public static final int TAG_IMAGE_READY_7_ROLLOVER                            = 0x1B5B;
-    public static final int TAG_IMAGE_READY_ROLLOVER                              = 0x1B5C;
-    public static final int TAG_IMAGE_READY_SAVE_LAYER_SETTINGS                   = 0x1B5D;
-    public static final int TAG_IMAGE_READY_VERSION                               = 0x1B5E;
-    public static final int TAG_LIGHTROOM_WORKFLOW                                = 0x1F40;
-    public static final int TAG_PRINT_FLAGS_INFO                                  = 0x2710;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_CHANNELS_ROWS_COLUMNS_DEPTH_MODE, "Channels, Rows, Columns, Depth, Mode");
-        _tagNameMap.put(TAG_MAC_PRINT_INFO, "Mac Print Info");
-        _tagNameMap.put(TAG_XML, "XML Data");
-        _tagNameMap.put(TAG_INDEXED_COLOR_TABLE, "Indexed Color Table");
-        _tagNameMap.put(TAG_RESOLUTION_INFO, "Resolution Info");
-        _tagNameMap.put(TAG_ALPHA_CHANNELS, "Alpha Channels");
-        _tagNameMap.put(TAG_DISPLAY_INFO_OBSOLETE, "Display Info (Obsolete)");
-        _tagNameMap.put(TAG_CAPTION, "Caption");
-        _tagNameMap.put(TAG_BORDER_INFORMATION, "Border Information");
-        _tagNameMap.put(TAG_BACKGROUND_COLOR, "Background Color");
-        _tagNameMap.put(TAG_PRINT_FLAGS, "Print Flags");
-        _tagNameMap.put(TAG_GRAYSCALE_AND_MULTICHANNEL_HALFTONING_INFORMATION, "Grayscale and Multichannel Halftoning Information");
-        _tagNameMap.put(TAG_COLOR_HALFTONING_INFORMATION, "Color Halftoning Information");
-        _tagNameMap.put(TAG_DUOTONE_HALFTONING_INFORMATION, "Duotone Halftoning Information");
-        _tagNameMap.put(TAG_GRAYSCALE_AND_MULTICHANNEL_TRANSFER_FUNCTION, "Grayscale and Multichannel Transfer Function");
-        _tagNameMap.put(TAG_COLOR_TRANSFER_FUNCTIONS, "Color Transfer Functions");
-        _tagNameMap.put(TAG_DUOTONE_TRANSFER_FUNCTIONS, "Duotone Transfer Functions");
-        _tagNameMap.put(TAG_DUOTONE_IMAGE_INFORMATION, "Duotone Image Information");
-        _tagNameMap.put(TAG_EFFECTIVE_BLACK_AND_WHITE_VALUES, "Effective Black and White Values");
-        _tagNameMap.put(TAG_EPS_OPTIONS, "EPS Options");
-        _tagNameMap.put(TAG_QUICK_MASK_INFORMATION, "Quick Mask Information");
-        _tagNameMap.put(TAG_LAYER_STATE_INFORMATION, "Layer State Information");
-        _tagNameMap.put(TAG_LAYERS_GROUP_INFORMATION, "Layers Group Information");
-        _tagNameMap.put(TAG_IPTC, "IPTC-NAA Record");
-        _tagNameMap.put(TAG_IMAGE_MODE_FOR_RAW_FORMAT_FILES, "Image Mode for Raw Format Files");
-        _tagNameMap.put(TAG_JPEG_QUALITY, "JPEG Quality");
-        _tagNameMap.put(TAG_GRID_AND_GUIDES_INFORMATION, "Grid and Guides Information");
-        _tagNameMap.put(TAG_THUMBNAIL_OLD, "Photoshop 4.0 Thumbnail");
-        _tagNameMap.put(TAG_COPYRIGHT, "Copyright Flag");
-        _tagNameMap.put(TAG_URL, "URL");
-        _tagNameMap.put(TAG_THUMBNAIL, "Thumbnail Data");
-        _tagNameMap.put(TAG_GLOBAL_ANGLE, "Global Angle");
-        _tagNameMap.put(TAG_ICC_PROFILE_BYTES, "ICC Profile Bytes");
-        _tagNameMap.put(TAG_WATERMARK, "Watermark");
-        _tagNameMap.put(TAG_ICC_UNTAGGED_PROFILE, "ICC Untagged Profile");
-        _tagNameMap.put(TAG_EFFECTS_VISIBLE, "Effects Visible");
-        _tagNameMap.put(TAG_SPOT_HALFTONE, "Spot Halftone");
-        _tagNameMap.put(TAG_SEED_NUMBER, "Seed Number");
-        _tagNameMap.put(TAG_UNICODE_ALPHA_NAMES, "Unicode Alpha Names");
-        _tagNameMap.put(TAG_INDEXED_COLOR_TABLE_COUNT, "Indexed Color Table Count");
-        _tagNameMap.put(TAG_TRANSPARENCY_INDEX, "Transparency Index");
-        _tagNameMap.put(TAG_GLOBAL_ALTITUDE, "Global Altitude");
-        _tagNameMap.put(TAG_SLICES, "Slices");
-        _tagNameMap.put(TAG_WORKFLOW_URL, "Workflow URL");
-        _tagNameMap.put(TAG_JUMP_TO_XPEP, "Jump To XPEP");
-        _tagNameMap.put(TAG_ALPHA_IDENTIFIERS, "Alpha Identifiers");
-        _tagNameMap.put(TAG_URL_LIST, "URL List");
-        _tagNameMap.put(TAG_VERSION, "Version Info");
-        _tagNameMap.put(TAG_EXIF_DATA_1, "EXIF Data 1");
-        _tagNameMap.put(TAG_EXIF_DATA_3, "EXIF Data 3");
-        _tagNameMap.put(TAG_XMP_DATA, "XMP Data");
-        _tagNameMap.put(TAG_CAPTION_DIGEST, "Caption Digest");
-        _tagNameMap.put(TAG_PRINT_SCALE, "Print Scale");
-        _tagNameMap.put(TAG_PIXEL_ASPECT_RATIO, "Pixel Aspect Ratio");
-        _tagNameMap.put(TAG_LAYER_COMPS, "Layer Comps");
-        _tagNameMap.put(TAG_ALTERNATE_DUOTONE_COLORS, "Alternate Duotone Colors");
-        _tagNameMap.put(TAG_ALTERNATE_SPOT_COLORS, "Alternate Spot Colors");
-        _tagNameMap.put(TAG_LAYER_SELECTION_IDS, "Layer Selection IDs");
-        _tagNameMap.put(TAG_HDR_TONING_INFO, "HDR Toning Info");
-        _tagNameMap.put(TAG_PRINT_INFO, "Print Info");
-        _tagNameMap.put(TAG_LAYER_GROUPS_ENABLED_ID, "Layer Groups Enabled ID");
-        _tagNameMap.put(TAG_COLOR_SAMPLERS, "Color Samplers");
-        _tagNameMap.put(TAG_MEASUREMENT_SCALE, "Measurement Scale");
-        _tagNameMap.put(TAG_TIMELINE_INFORMATION, "Timeline Information");
-        _tagNameMap.put(TAG_SHEET_DISCLOSURE, "Sheet Disclosure");
-        _tagNameMap.put(TAG_DISPLAY_INFO, "Display Info");
-        _tagNameMap.put(TAG_ONION_SKINS, "Onion Skins");
-        _tagNameMap.put(TAG_COUNT_INFORMATION, "Count information");
-        _tagNameMap.put(TAG_PRINT_INFO_2, "Print Info 2");
-        _tagNameMap.put(TAG_PRINT_STYLE, "Print Style");
-        _tagNameMap.put(TAG_MAC_NSPRINTINFO, "Mac NSPrintInfo");
-        _tagNameMap.put(TAG_WIN_DEVMODE, "Win DEVMODE");
-        _tagNameMap.put(TAG_AUTO_SAVE_FILE_PATH, "Auto Save File Subpath");
-        _tagNameMap.put(TAG_AUTO_SAVE_FORMAT, "Auto Save Format");
-        _tagNameMap.put(TAG_PATH_SELECTION_STATE, "Subpath Selection State");
-
-        _tagNameMap.put(TAG_CLIPPING_PATH_NAME, "Clipping Path Name");
-        _tagNameMap.put(TAG_ORIGIN_PATH_INFO, "Origin Subpath Info");
-        _tagNameMap.put(TAG_IMAGE_READY_VARIABLES_XML, "Image Ready Variables XML");
-        _tagNameMap.put(TAG_IMAGE_READY_DATA_SETS, "Image Ready Data Sets");
-        _tagNameMap.put(TAG_IMAGE_READY_SELECTED_STATE, "Image Ready Selected State");
-        _tagNameMap.put(TAG_IMAGE_READY_7_ROLLOVER, "Image Ready 7 Rollover Expanded State");
-        _tagNameMap.put(TAG_IMAGE_READY_ROLLOVER, "Image Ready Rollover Expanded State");
-        _tagNameMap.put(TAG_IMAGE_READY_SAVE_LAYER_SETTINGS, "Image Ready Save Layer Settings");
-        _tagNameMap.put(TAG_IMAGE_READY_VERSION, "Image Ready Version");
-        _tagNameMap.put(TAG_LIGHTROOM_WORKFLOW, "Lightroom Workflow");
-        _tagNameMap.put(TAG_PRINT_FLAGS_INFO, "Print Flags Information");
-    }
-
-    public PhotoshopDirectory()
-    {
-        this.setDescriptor(new PhotoshopDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "Photoshop";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-
-    @Nullable
-    public byte[] getThumbnailBytes()
-    {
-        byte[] storedBytes = getByteArray(PhotoshopDirectory.TAG_THUMBNAIL);
-        if (storedBytes == null)
-            storedBytes = getByteArray(PhotoshopDirectory.TAG_THUMBNAIL_OLD);
-        if (storedBytes == null || storedBytes.length <= 28)
-            return null;
-
-        int thumbSize = storedBytes.length - 28;
-        byte[] thumbBytes = new byte[thumbSize];
-        System.arraycopy(storedBytes, 28, thumbBytes, 0, thumbSize);
-        return thumbBytes;
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PhotoshopReader.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PhotoshopReader.java	(revision 16024)
+++ 	(revision )
@@ -1,180 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.photoshop;
-
-import java.util.Arrays;
-import java.util.Collections;
-
-import com.drew.imaging.ImageProcessingException;
-import com.drew.imaging.jpeg.JpegSegmentMetadataReader;
-import com.drew.imaging.jpeg.JpegSegmentType;
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.exif.ExifReader;
-import com.drew.metadata.icc.IccReader;
-import com.drew.metadata.iptc.IptcReader;
-//import com.drew.metadata.xmp.XmpReader;
-
-/**
- * Reads metadata created by Photoshop and stored in the APPD segment of JPEG files.
- * Note that IPTC data may be stored within this segment, in which case this reader will
- * create both a {@link PhotoshopDirectory} and a {@link com.drew.metadata.iptc.IptcDirectory}.
- *
- * @author Drew Noakes https://drewnoakes.com
- * @author Yuri Binev
- * @author Payton Garland
- */
-public class PhotoshopReader implements JpegSegmentMetadataReader
-{
-    @NotNull
-    private static final String JPEG_SEGMENT_PREAMBLE = "Photoshop 3.0";
-
-    @NotNull
-    public Iterable<JpegSegmentType> getSegmentTypes()
-    {
-        return Collections.singletonList(JpegSegmentType.APPD);
-    }
-
-    public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
-    {
-        final int preambleLength = JPEG_SEGMENT_PREAMBLE.length();
-
-        for (byte[] segmentBytes : segments) {
-            // Ensure data starts with the necessary preamble
-            if (segmentBytes.length < preambleLength + 1 || !JPEG_SEGMENT_PREAMBLE.equals(new String(segmentBytes, 0, preambleLength)))
-                continue;
-
-            extract(
-                new SequentialByteArrayReader(segmentBytes, preambleLength + 1),
-                segmentBytes.length - preambleLength - 1,
-                metadata);
-        }
-    }
-
-    public void extract(@NotNull final SequentialReader reader, int length, @NotNull final Metadata metadata)
-    {
-        extract(reader, length, metadata, null);
-    }
-
-    public void extract(@NotNull final SequentialReader reader, int length, @NotNull final Metadata metadata, @Nullable final Directory parentDirectory)
-    {
-        PhotoshopDirectory directory = new PhotoshopDirectory();
-        metadata.addDirectory(directory);
-
-        if (parentDirectory != null)
-            directory.setParent(parentDirectory);
-
-        // Data contains a sequence of Image Resource Blocks (IRBs):
-        //
-        // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found
-        // 2 bytes - Resource identifier
-        // String  - Pascal string, padded to make length even
-        // 4 bytes - Size of resource data which follows
-        // Data    - The resource data, padded to make size even
-        //
-        // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504
-
-        int pos = 0;
-        int clippingPathCount = 0;
-        while (pos < length) {
-            try {
-                // 4 bytes for the signature ("8BIM", "PHUT", etc.)
-                String signature = reader.getString(4);
-                pos += 4;
-
-                // 2 bytes for the resource identifier (tag type).
-                int tagType = reader.getUInt16(); // segment type
-                pos += 2;
-
-                // A variable number of bytes holding a pascal string (two leading bytes for length).
-                short descriptionLength = reader.getUInt8();
-                pos += 1;
-                // Some basic bounds checking
-                if (descriptionLength < 0 || descriptionLength + pos > length)
-                    throw new ImageProcessingException("Invalid string length");
-
-                // Get name (important for paths)
-                StringBuilder description = new StringBuilder();
-                descriptionLength += pos;
-                // Loop through each byte and append to string
-                while (pos < descriptionLength) {
-                    description.append((char)reader.getUInt8());
-                    pos ++;
-                }
-
-                // The number of bytes is padded with a trailing zero, if needed, to make the size even.
-                if (pos % 2 != 0) {
-                    reader.skip(1);
-                    pos++;
-                }
-
-                // 4 bytes for the size of the resource data that follows.
-                int byteCount = reader.getInt32();
-                pos += 4;
-                // The resource data.
-                byte[] tagBytes = reader.getBytes(byteCount);
-                pos += byteCount;
-                // The number of bytes is padded with a trailing zero, if needed, to make the size even.
-                if (pos % 2 != 0) {
-                    reader.skip(1);
-                    pos++;
-                }
-
-                if (signature.equals("8BIM")) {
-                    if (tagType == PhotoshopDirectory.TAG_IPTC)
-                        new IptcReader().extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.length, directory);
-                    else if (tagType == PhotoshopDirectory.TAG_ICC_PROFILE_BYTES)
-                        new IccReader().extract(new ByteArrayReader(tagBytes), metadata, directory);
-                    else if (tagType == PhotoshopDirectory.TAG_EXIF_DATA_1 || tagType == PhotoshopDirectory.TAG_EXIF_DATA_3)
-                        new ExifReader().extract(new ByteArrayReader(tagBytes), metadata, 0, directory);
-                    //else if (tagType == PhotoshopDirectory.TAG_XMP_DATA)
-                    //    new XmpReader().extract(tagBytes, metadata, directory);
-                    else if (tagType >= 0x07D0 && tagType <= 0x0BB6) {
-                        clippingPathCount++;
-                        tagBytes = Arrays.copyOf(tagBytes, tagBytes.length + description.length() + 1);
-                        // Append description(name) to end of byte array with 1 byte before the description representing the length
-                        for (int i = tagBytes.length - description.length() - 1; i < tagBytes.length; i++) {
-                            if (i % (tagBytes.length - description.length() - 1 + description.length()) == 0)
-                                tagBytes[i] = (byte)description.length();
-                            else
-                                tagBytes[i] = (byte)description.charAt(i - (tagBytes.length - description.length() - 1));
-                        }
-                        PhotoshopDirectory._tagNameMap.put(0x07CF + clippingPathCount, "Path Info " + clippingPathCount);
-                        directory.setByteArray(0x07CF + clippingPathCount, tagBytes);
-                    }
-                    else
-                        directory.setByteArray(tagType, tagBytes);
-
-                    if (tagType >= 0x0fa0 && tagType <= 0x1387)
-                        PhotoshopDirectory._tagNameMap.put(tagType, String.format("Plug-in %d Data", tagType - 0x0fa0 + 1));
-                }
-            } catch (Exception ex) {
-                directory.addError(ex.getMessage());
-                return;
-            }
-        }
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PhotoshopTiffHandler.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PhotoshopTiffHandler.java	(revision 16024)
+++ 	(revision )
@@ -1,62 +1,0 @@
-package com.drew.metadata.photoshop;
-
-import java.io.IOException;
-import java.util.Set;
-
-import com.drew.lang.ByteArrayReader;
-import com.drew.lang.RandomAccessReader;
-import com.drew.lang.SequentialByteArrayReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.exif.ExifTiffHandler;
-import com.drew.metadata.icc.IccReader;
-//import com.drew.metadata.xmp.XmpReader;
-
-/**
- * @author Payton Garland
- */
-public class PhotoshopTiffHandler extends ExifTiffHandler
-{
-    // Photoshop-specific Tiff Tags
-    // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1039502
-    private static final int TAG_PAGE_MAKER_EXTENSION = 0x014A;
-    private static final int TAG_JPEG_TABLES = 0X01B5;
-    private static final int TAG_XMP = 0x02BC;
-    private static final int TAG_FILE_INFORMATION = 0x83BB;
-    private static final int TAG_PHOTOSHOP_IMAGE_RESOURCES = 0x8649;
-    private static final int TAG_EXIF_IFD_POINTER = 0x8769;
-    private static final int TAG_ICC_PROFILES = 0x8773;
-    private static final int TAG_EXIF_GPS = 0x8825;
-    private static final int TAG_T_IMAGE_SOURCE_DATA = 0x935C;
-    private static final int TAG_T_ANNOTATIONS = 0xC44F;
-
-    public PhotoshopTiffHandler(Metadata metadata, Directory parentDirectory)
-    {
-        super(metadata, parentDirectory);
-    }
-
-    @Override
-    public boolean customProcessTag(final int tagOffset,
-                                    final @NotNull Set<Integer> processedIfdOffsets,
-                                    final int tiffHeaderOffset,
-                                    final @NotNull RandomAccessReader reader,
-                                    final int tagId,
-                                    final int byteCount) throws IOException
-    {
-        switch (tagId) {
-            //case TAG_XMP:
-            //    new XmpReader().extract(reader.getBytes(tagOffset, byteCount), _metadata);
-            //    return true;
-            case TAG_PHOTOSHOP_IMAGE_RESOURCES:
-                new PhotoshopReader().extract(new SequentialByteArrayReader(reader.getBytes(tagOffset, byteCount)), byteCount, _metadata);
-                return true;
-            case TAG_ICC_PROFILES:
-                new IccReader().extract(new ByteArrayReader(reader.getBytes(tagOffset, byteCount)), _metadata);
-                return true;
-        }
-
-
-        return super.customProcessTag(tagOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, byteCount);
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PsdHeaderDescriptor.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PsdHeaderDescriptor.java	(revision 16024)
+++ 	(revision )
@@ -1,117 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.photoshop;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.TagDescriptor;
-
-import static com.drew.metadata.photoshop.PsdHeaderDirectory.*;
-
-/**
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PsdHeaderDescriptor extends TagDescriptor<PsdHeaderDirectory>
-{
-    public PsdHeaderDescriptor(@NotNull PsdHeaderDirectory directory)
-    {
-        super(directory);
-    }
-
-    @Override
-    public String getDescription(int tagType)
-    {
-        switch (tagType) {
-            case TAG_CHANNEL_COUNT:
-                return getChannelCountDescription();
-            case TAG_BITS_PER_CHANNEL:
-                return getBitsPerChannelDescription();
-            case TAG_COLOR_MODE:
-                return getColorModeDescription();
-            case TAG_IMAGE_HEIGHT:
-                return getImageHeightDescription();
-            case TAG_IMAGE_WIDTH:
-                return getImageWidthDescription();
-            default:
-                return super.getDescription(tagType);
-        }
-    }
-
-    @Nullable
-    public String getChannelCountDescription()
-    {
-        // Supported range is 1 to 56.
-        Integer value = _directory.getInteger(TAG_CHANNEL_COUNT);
-        if (value == null)
-            return null;
-        return value + " channel" + (value == 1 ? "" : "s");
-    }
-
-    @Nullable
-    public String getBitsPerChannelDescription()
-    {
-        // Supported values are 1, 8, 16 and 32.
-        Integer value = _directory.getInteger(TAG_BITS_PER_CHANNEL);
-        if (value == null)
-            return null;
-        return value + " bit" + (value == 1 ? "" : "s") + " per channel";
-    }
-
-    @Nullable
-    public String getColorModeDescription()
-    {
-        return getIndexedDescription(TAG_COLOR_MODE,
-            "Bitmap",
-            "Grayscale",
-            "Indexed",
-            "RGB",
-            "CMYK",
-            null,
-            null,
-            "Multichannel",
-            "Duotone",
-            "Lab");
-    }
-
-    @Nullable
-    public String getImageHeightDescription()
-    {
-        Integer value = _directory.getInteger(TAG_IMAGE_HEIGHT);
-        if (value == null)
-            return null;
-        return value + " pixel" + (value == 1 ? "" : "s");
-    }
-
-    @Nullable
-    public String getImageWidthDescription()
-    {
-        try {
-            Integer value = _directory.getInteger(TAG_IMAGE_WIDTH);
-            if (value == null)
-                return null;
-            return value + " pixel" + (value == 1 ? "" : "s");
-        } catch (Exception e) {
-            return null;
-        }
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PsdHeaderDirectory.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PsdHeaderDirectory.java	(revision 16024)
+++ 	(revision )
@@ -1,88 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.photoshop;
-
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Directory;
-
-import java.util.HashMap;
-
-/**
- * Holds the basic metadata found in the header of a Photoshop PSD file.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-@SuppressWarnings("WeakerAccess")
-public class PsdHeaderDirectory extends Directory
-{
-    /**
-     * The number of channels in the image, including any alpha channels. Supported range is 1 to 56.
-     */
-    public static final int TAG_CHANNEL_COUNT = 1;
-    /**
-     * The height of the image in pixels.
-     */
-    public static final int TAG_IMAGE_HEIGHT = 2;
-    /**
-     * The width of the image in pixels.
-     */
-    public static final int TAG_IMAGE_WIDTH = 3;
-    /**
-     * The number of bits per channel. Supported values are 1, 8, 16 and 32.
-     */
-    public static final int TAG_BITS_PER_CHANNEL = 4;
-    /**
-     * The color mode of the file. Supported values are:
-     * Bitmap = 0; Grayscale = 1; Indexed = 2; RGB = 3; CMYK = 4; Multichannel = 7; Duotone = 8; Lab = 9.
-     */
-    public static final int TAG_COLOR_MODE = 5;
-
-    @NotNull
-    protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
-
-    static {
-        _tagNameMap.put(TAG_CHANNEL_COUNT, "Channel Count");
-        _tagNameMap.put(TAG_IMAGE_HEIGHT, "Image Height");
-        _tagNameMap.put(TAG_IMAGE_WIDTH, "Image Width");
-        _tagNameMap.put(TAG_BITS_PER_CHANNEL, "Bits Per Channel");
-        _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
-    }
-
-    public PsdHeaderDirectory()
-    {
-        this.setDescriptor(new PsdHeaderDescriptor(this));
-    }
-
-    @Override
-    @NotNull
-    public String getName()
-    {
-        return "PSD Header";
-    }
-
-    @Override
-    @NotNull
-    protected HashMap<Integer, String> getTagNameMap()
-    {
-        return _tagNameMap;
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/PsdReader.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/PsdReader.java	(revision 16024)
+++ 	(revision )
@@ -1,121 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-
-package com.drew.metadata.photoshop;
-
-import com.drew.lang.SequentialReader;
-import com.drew.lang.annotations.NotNull;
-import com.drew.metadata.Metadata;
-
-import java.io.IOException;
-
-/**
- * Reads metadata stored within PSD file format data.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public class PsdReader
-{
-    public void extract(@NotNull final SequentialReader reader, @NotNull final Metadata metadata)
-    {
-        PsdHeaderDirectory directory = new PsdHeaderDirectory();
-        metadata.addDirectory(directory);
-
-        // FILE HEADER SECTION
-
-        try {
-            final int signature = reader.getInt32();
-            if (signature != 0x38425053) // "8BPS"
-            {
-                directory.addError("Invalid PSD file signature");
-                return;
-            }
-
-            final int version = reader.getUInt16();
-            if (version != 1 && version != 2)
-            {
-                directory.addError("Invalid PSD file version (must be 1 or 2)");
-                return;
-            }
-
-            // 6 reserved bytes are skipped here.  They should be zero.
-            reader.skip(6);
-
-            final int channelCount = reader.getUInt16();
-            directory.setInt(PsdHeaderDirectory.TAG_CHANNEL_COUNT, channelCount);
-
-            // even though this is probably an unsigned int, the max height in practice is 300,000
-            final int imageHeight = reader.getInt32();
-            directory.setInt(PsdHeaderDirectory.TAG_IMAGE_HEIGHT, imageHeight);
-
-            // even though this is probably an unsigned int, the max width in practice is 300,000
-            final int imageWidth = reader.getInt32();
-            directory.setInt(PsdHeaderDirectory.TAG_IMAGE_WIDTH, imageWidth);
-
-            final int bitsPerChannel = reader.getUInt16();
-            directory.setInt(PsdHeaderDirectory.TAG_BITS_PER_CHANNEL, bitsPerChannel);
-
-            final int colorMode = reader.getUInt16();
-            directory.setInt(PsdHeaderDirectory.TAG_COLOR_MODE, colorMode);
-        } catch (IOException e) {
-            directory.addError("Unable to read PSD header");
-            return;
-        }
-
-        // COLOR MODE DATA SECTION
-
-        try {
-            long sectionLength = reader.getUInt32();
-
-            /*
-             * Only indexed color and duotone (see the mode field in the File header section) have color mode data.
-             * For all other modes, this section is just the 4-byte length field, which is set to zero.
-             *
-             * Indexed color images: length is 768; color data contains the color table for the image,
-             *                       in non-interleaved order.
-             * Duotone images: color data contains the duotone specification (the format of which is not documented).
-             *                 Other applications that read Photoshop files can treat a duotone image as a gray	image,
-             *                 and just preserve the contents of the duotone information when reading and writing the
-             *                 file.
-             */
-
-            reader.skip(sectionLength);
-        } catch (IOException e) {
-            return;
-        }
-
-        // IMAGE RESOURCES SECTION
-
-        try {
-            long sectionLength = reader.getUInt32();
-
-            assert(sectionLength <= Integer.MAX_VALUE);
-
-            new PhotoshopReader().extract(reader, (int)sectionLength, metadata);
-        } catch (IOException e) {
-            // ignore
-        }
-
-        // LAYER AND MASK INFORMATION SECTION (skipped)
-
-        // IMAGE DATA SECTION (skipped)
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/Subpath.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/Subpath.java	(revision 16024)
+++ 	(revision )
@@ -1,58 +1,0 @@
-package com.drew.metadata.photoshop;
-
-import java.util.ArrayList;
-
-/**
- * Represents a subpath created by Photoshop:
- * <ul>
- *   <li>Closed Bezier knot, linked</li>
- *   <li>Closed Bezier knot, unlinked</li>
- *   <li>Open Bezier knot, linked</li>
- *   <li>Open Bezier knot, unlinked</li>
- * </ul>
- *
- * @author Payton Garland
- */
-public class Subpath
-{
-    private final ArrayList<Knot> _knots = new ArrayList<Knot>();
-    private final String _type;
-
-    public Subpath()
-    {
-        this("");
-    }
-
-    public Subpath(String type)
-    {
-        _type = type;
-    }
-
-    /**
-     * Appends a knot (set of 3 points) into the list
-     */
-    public void add(Knot knot)
-    {
-        _knots.add(knot);
-    }
-
-    /**
-     * Gets size of knots list
-     *
-     * @return size of knots ArrayList
-     */
-    public int size()
-    {
-        return _knots.size();
-    }
-
-    public Iterable<Knot> getKnots()
-    {
-        return _knots;
-    }
-
-    public String getType()
-    {
-        return _type;
-    }
-}
Index: unk/src/com/drew/metadata/photoshop/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/photoshop/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of Photoshop metadata.
- */
-package com.drew.metadata.photoshop;
Index: unk/src/com/drew/metadata/tiff/DirectoryTiffHandler.java
===================================================================
--- /trunk/src/com/drew/metadata/tiff/DirectoryTiffHandler.java	(revision 16024)
+++ 	(revision )
@@ -1,218 +1,0 @@
-/*
- * Copyright 2002-2019 Drew Noakes and contributors
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- *
- * More information about this project is available at:
- *
- *    https://drewnoakes.com/code/exif/
- *    https://github.com/drewnoakes/metadata-extractor
- */
-package com.drew.metadata.tiff;
-
-import com.drew.imaging.tiff.TiffHandler;
-import com.drew.lang.Rational;
-import com.drew.lang.annotations.NotNull;
-import com.drew.lang.annotations.Nullable;
-import com.drew.metadata.Directory;
-import com.drew.metadata.ErrorDirectory;
-import com.drew.metadata.Metadata;
-import com.drew.metadata.StringValue;
-
-import java.util.Stack;
-
-/**
- * Adapter between the {@link TiffHandler} interface and the {@link Metadata}/{@link Directory} object model.
- *
- * @author Drew Noakes https://drewnoakes.com
- */
-public abstract class DirectoryTiffHandler implements TiffHandler
-{
-    private final Stack<Directory> _directoryStack = new Stack<Directory>();
-
-    @Nullable private Directory _rootParentDirectory;
-    @Nullable protected Directory _currentDirectory;
-    protected final Metadata _metadata;
-
-    protected DirectoryTiffHandler(Metadata metadata, @Nullable Directory parentDirectory)
-    {
-        _metadata = metadata;
-        _rootParentDirectory = parentDirectory;
-    }
-
-    public void endingIFD()
-    {
-        _currentDirectory = _directoryStack.empty() ? null : _directoryStack.pop();
-    }
-
-    protected void pushDirectory(@NotNull Class<? extends Directory> directoryClass)
-    {
-        Directory newDirectory;
-
-        try {
-            newDirectory = directoryClass.newInstance();
-        } catch (InstantiationException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-
-        // If this is the first directory, don't add to the stack
-        if (_currentDirectory == null) {
-            // Apply any pending root parent to this new directory
-            if (_rootParentDirectory != null) {
-                newDirectory.setParent(_rootParentDirectory);
-                _rootParentDirectory = null;
-            }
-        }
-        else {
-            // The current directory is pushed onto the stack, and set as the new directory's parent
-            _directoryStack.push(_currentDirectory);
-            newDirectory.setParent(_currentDirectory);
-        }
-
-        _currentDirectory = newDirectory;
-        _metadata.addDirectory(_currentDirectory);
-    }
-
-    public void warn(@NotNull String message)
-    {
-        getCurrentOrErrorDirectory().addError(message);
-    }
-
-    public void error(@NotNull String message)
-    {
-        getCurrentOrErrorDirectory().addError(message);
-    }
-
-    @NotNull
-    private Directory getCurrentOrErrorDirectory()
-    {
-        if (_currentDirectory != null)
-            return _currentDirectory;
-        ErrorDirectory error = _metadata.getFirstDirectoryOfType(ErrorDirectory.class);
-        if (error != null)
-            return error;
-        pushDirectory(ErrorDirectory.class);
-        return _currentDirectory;
-    }
-
-    public void setByteArray(int tagId, @NotNull byte[] bytes)
-    {
-        _currentDirectory.setByteArray(tagId, bytes);
-    }
-
-    public void setString(int tagId, @NotNull StringValue string)
-    {
-        _currentDirectory.setStringValue(tagId, string);
-    }
-
-    public void setRational(int tagId, @NotNull Rational rational)
-    {
-        _currentDirectory.setRational(tagId, rational);
-    }
-
-    public void setRationalArray(int tagId, @NotNull Rational[] array)
-    {
-        _currentDirectory.setRationalArray(tagId, array);
-    }
-
-    public void setFloat(int tagId, float float32)
-    {
-        _currentDirectory.setFloat(tagId, float32);
-    }
-
-    public void setFloatArray(int tagId, @NotNull float[] array)
-    {
-        _currentDirectory.setFloatArray(tagId, array);
-    }
-
-    public void setDouble(int tagId, double double64)
-    {
-        _currentDirectory.setDouble(tagId, double64);
-    }
-
-    public void setDoubleArray(int tagId, @NotNull double[] array)
-    {
-        _currentDirectory.setDoubleArray(tagId, array);
-    }
-
-    public void setInt8s(int tagId, byte int8s)
-    {
-        // NOTE Directory stores all integral types as int32s, except for int32u and long
-        _currentDirectory.setInt(tagId, int8s);
-    }
-
-    public void setInt8sArray(int tagId, @NotNull byte[] array)
-    {
-        // NOTE Directory stores all integral types as int32s, except for int32u and long
-        _currentDirectory.setByteArray(tagId, array);
-    }
-
-    public void setInt8u(int tagId, short int8u)
-    {
-        // NOTE Directory stores all integral types as int32s, except for int32u and long
-        _currentDirectory.setInt(tagId, int8u);
-    }
-
-    public void setInt8uArray(int tagId, @NotNull short[] array)
-    {
-        // TODO create and use a proper setter for short[]
-        _currentDirectory.setObjectArray(tagId, array);
-    }
-
-    public void setInt16s(int tagId, int int16s)
-    {
-        // TODO create and use a proper setter for int16u?
-        _currentDirectory.setInt(tagId, int16s);
-    }
-
-    public void setInt16sArray(int tagId, @NotNull short[] array)
-    {
-        // TODO create and use a proper setter for short[]
-        _currentDirectory.setObjectArray(tagId, array);
-    }
-
-    public void setInt16u(int tagId, int int16u)
-    {
-        // TODO create and use a proper setter for
-        _currentDirectory.setInt(tagId, int16u);
-    }
-
-    public void setInt16uArray(int tagId, @NotNull int[] array)
-    {
-        // TODO create and use a proper setter for short[]
-        _currentDirectory.setObjectArray(tagId, array);
-    }
-
-    public void setInt32s(int tagId, int int32s)
-    {
-        _currentDirectory.setInt(tagId, int32s);
-    }
-
-    public void setInt32sArray(int tagId, @NotNull int[] array)
-    {
-        _currentDirectory.setIntArray(tagId, array);
-    }
-
-    public void setInt32u(int tagId, long int32u)
-    {
-        _currentDirectory.setLong(tagId, int32u);
-    }
-
-    public void setInt32uArray(int tagId, @NotNull long[] array)
-    {
-        // TODO create and use a proper setter for short[]
-        _currentDirectory.setObjectArray(tagId, array);
-    }
-}
Index: unk/src/com/drew/metadata/tiff/package-info.java
===================================================================
--- /trunk/src/com/drew/metadata/tiff/package-info.java	(revision 16024)
+++ 	(revision )
@@ -1,6 +1,0 @@
-/**
- * Contains classes for the extraction and modelling of TIFF file metadata.
- *
- * @since 2.7.0
- */
-package com.drew.metadata.tiff;
