You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by pa...@apache.org on 2018/06/02 04:50:10 UTC

[drill] 02/10: DRILL-4364: Image Metadata Format Plugin - Initial commit of Image Metadata Format Plugin - See https://issues.apache.org/jira/browse/DRILL-4364

This is an automated email from the ASF dual-hosted git repository.

parthc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git

commit 04a532d2d8790d69214adbb4a8247f8a382cfd08
Author: Akihiko Kusanagi <ak...@mapr.com>
AuthorDate: Sun Feb 7 03:55:57 2016 +0900

    DRILL-4364: Image Metadata Format Plugin - Initial commit of Image Metadata Format Plugin - See https://issues.apache.org/jira/browse/DRILL-4364
    
    This closes #367
---
 exec/java-exec/pom.xml                             |   5 +
 .../store/image/GenericMetadataDescriptor.java     |  89 ++++
 .../exec/store/image/GenericMetadataDirectory.java | 315 +++++++++++++
 .../exec/store/image/GenericMetadataReader.java    | 412 +++++++++++++++++
 .../drill/exec/store/image/ImageFormatConfig.java  |  97 ++++
 .../drill/exec/store/image/ImageFormatPlugin.java  |  82 ++++
 .../drill/exec/store/image/ImageRecordReader.java  | 493 +++++++++++++++++++++
 .../main/resources/bootstrap-storage-plugins.json  |  18 +
 .../store/dfs/TestFormatPluginOptionExtractor.java |   7 +
 .../exec/store/image/TestImageRecordReader.java    | 128 ++++++
 .../src/test/resources/store/image/1_webp_a.webp   | Bin 0 -> 23404 bytes
 .../src/test/resources/store/image/adobeJpeg1.eps  | Bin 0 -> 99569 bytes
 .../src/test/resources/store/image/avi.json        |  32 ++
 .../src/test/resources/store/image/bmp.json        |  36 ++
 .../src/test/resources/store/image/eps.json        | 116 +++++
 .../src/test/resources/store/image/gif.json        |  47 ++
 .../src/test/resources/store/image/ico.json        |  33 ++
 .../src/test/resources/store/image/jpeg.json       | 213 +++++++++
 .../src/test/resources/store/image/mov.json        |  67 +++
 .../src/test/resources/store/image/mp4.json        |  56 +++
 .../src/test/resources/store/image/pcx.json        |  37 ++
 .../src/test/resources/store/image/png.json        |  57 +++
 .../src/test/resources/store/image/psd.json        | 119 +++++
 .../store/image/rose-128x174-24bit-lzw.tiff        | Bin 0 -> 50476 bytes
 .../resources/store/image/rose-128x174-24bit.bmp   | Bin 0 -> 66872 bytes
 .../resources/store/image/rose-128x174-24bit.pcx   | Bin 0 -> 34864 bytes
 .../store/image/rose-128x174-32bit-alpha.png       | Bin 0 -> 26308 bytes
 .../store/image/rose-128x174-32bit-alpha.psd       | Bin 0 -> 102618 bytes
 .../store/image/rose-128x174-8bit-alpha.gif        | Bin 0 -> 10463 bytes
 .../store/image/rose-32x32-32bit-alpha.ico         | Bin 0 -> 4286 bytes
 .../src/test/resources/store/image/sample.avi      | Bin 0 -> 375688 bytes
 .../src/test/resources/store/image/sample.mov      | Bin 0 -> 469690 bytes
 .../src/test/resources/store/image/sample.mp4      | Bin 0 -> 383631 bytes
 .../src/test/resources/store/image/sample.wav      | Bin 0 -> 37534 bytes
 .../src/test/resources/store/image/tiff.json       |  87 ++++
 .../src/test/resources/store/image/wav.json        |  32 ++
 .../src/test/resources/store/image/webp.json       |  29 ++
 .../test/resources/store/image/withExifAndIptc.jpg | Bin 0 -> 44606 bytes
 exec/jdbc-all/pom.xml                              |   4 +
 39 files changed, 2611 insertions(+)

diff --git a/exec/java-exec/pom.xml b/exec/java-exec/pom.xml
index 345e240..0d03cc8 100644
--- a/exec/java-exec/pom.xml
+++ b/exec/java-exec/pom.xml
@@ -632,6 +632,11 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <dependency>
+      <groupId>com.drewnoakes</groupId>
+      <artifactId>metadata-extractor</artifactId>
+      <version>2.11.0</version>
+    </dependency>
   </dependencies>
 
   <profiles>
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataDescriptor.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataDescriptor.java
new file mode 100644
index 0000000..82d42fd
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataDescriptor.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import com.drew.lang.annotations.NotNull;
+import com.drew.lang.annotations.Nullable;
+import com.drew.metadata.TagDescriptor;
+
+import static org.apache.drill.exec.store.image.GenericMetadataDirectory.TAG_FILE_SIZE;
+import static org.apache.drill.exec.store.image.GenericMetadataDirectory.TAG_ORIENTATION;
+import static org.apache.drill.exec.store.image.GenericMetadataDirectory.TAG_DURATION;
+
+@SuppressWarnings("WeakerAccess")
+public class GenericMetadataDescriptor extends TagDescriptor<GenericMetadataDirectory>
+{
+  public GenericMetadataDescriptor(@NotNull GenericMetadataDirectory directory)
+  {
+    super(directory);
+  }
+
+  @Override
+  @Nullable
+  public String getDescription(int tagType)
+  {
+    switch (tagType) {
+      case TAG_FILE_SIZE:
+        return getFileSizeDescription();
+      case TAG_ORIENTATION:
+        return getOrientationDescription();
+      case TAG_DURATION:
+        return getDurationDescription();
+      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";
+  }
+
+  @Nullable
+  private String getOrientationDescription() {
+    return getIndexedDescription(TAG_ORIENTATION, 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
+  private String getDurationDescription() {
+    Long value = _directory.getLongObject(TAG_DURATION);
+    if (value == null) {
+      return null;
+    }
+
+    Integer hours = (int)(value / (Math.pow(60, 2)));
+    Integer minutes = (int)((value / (Math.pow(60, 1))) - (hours * 60));
+    Integer seconds = (int)Math.ceil((value / (Math.pow(60, 0))) - (minutes * 60));
+    return String.format("%1$02d:%2$02d:%3$02d", hours, minutes, seconds);
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataDirectory.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataDirectory.java
new file mode 100644
index 0000000..871a11b
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataDirectory.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import com.drew.lang.annotations.NotNull;
+import com.drew.metadata.Directory;
+import com.drew.metadata.MetadataException;
+
+import java.util.HashMap;
+
+@SuppressWarnings("WeakerAccess")
+public class GenericMetadataDirectory extends Directory
+{
+  public static final int TAG_FILE_SIZE = 1;
+  public static final int TAG_FILE_DATE_TIME = 2;
+  public static final int TAG_FORMAT = 3;
+  public static final int TAG_PIXEL_WIDTH = 4;
+  public static final int TAG_PIXEL_HEIGHT = 5;
+  public static final int TAG_ORIENTATION = 6;
+  public static final int TAG_DPI_WIDTH = 7;
+  public static final int TAG_DPI_HEIGHT = 8;
+  public static final int TAG_COLOR_MODE = 9;
+  public static final int TAG_BITS_PER_PIXEL = 10;
+  public static final int TAG_HAS_ALPHA = 11;
+  public static final int TAG_DURATION = 12;
+  public static final int TAG_VIDEO_CODEC = 13;
+  public static final int TAG_FRAME_RATE = 14;
+  public static final int TAG_AUDIO_CODEC = 15;
+  public static final int TAG_AUDIO_SAMPLE_SIZE = 16;
+  public static final int TAG_AUDIO_SAMPLE_RATE = 17;
+
+  @NotNull
+  protected static final HashMap<Integer, String> _tagNameMap = new HashMap<>();
+
+  static {
+    _tagNameMap.put(TAG_FILE_SIZE, "File Size");
+    _tagNameMap.put(TAG_FILE_DATE_TIME, "File Date Time");
+    _tagNameMap.put(TAG_FORMAT, "Format");
+    _tagNameMap.put(TAG_PIXEL_WIDTH, "Pixel Width");
+    _tagNameMap.put(TAG_PIXEL_HEIGHT, "Pixel Height");
+    _tagNameMap.put(TAG_ORIENTATION, "Orientaion");
+    _tagNameMap.put(TAG_DPI_WIDTH, "DPI Width");
+    _tagNameMap.put(TAG_DPI_HEIGHT, "DPI Height");
+    _tagNameMap.put(TAG_COLOR_MODE, "Color Mode");
+    _tagNameMap.put(TAG_BITS_PER_PIXEL, "Bits Per Pixel");
+    _tagNameMap.put(TAG_HAS_ALPHA, "Has Alpha");
+    _tagNameMap.put(TAG_DURATION, "Duration");
+    _tagNameMap.put(TAG_VIDEO_CODEC, "Video Codec");
+    _tagNameMap.put(TAG_FRAME_RATE, "Frame Rate");
+    _tagNameMap.put(TAG_AUDIO_CODEC, "Audio Codec");
+    _tagNameMap.put(TAG_AUDIO_SAMPLE_SIZE, "Audio Sample Size");
+    _tagNameMap.put(TAG_AUDIO_SAMPLE_RATE, "Audio Sample Rate");
+  }
+
+  public GenericMetadataDirectory()
+  {
+    this.setDescriptor(new GenericMetadataDescriptor(this));
+  }
+
+  @Override
+  @NotNull
+  public String getName()
+  {
+    return "Generic Metadata";
+  }
+
+  @Override
+  @NotNull
+  protected HashMap<Integer, String> getTagNameMap()
+  {
+    return _tagNameMap;
+  }
+
+  private void setIntIfEmpty(int tagType, int value) {
+    if (!containsTag(tagType)) {
+      setInt(tagType, value);
+    }
+  }
+
+  private void setLongIfEmpty(int tagType, long value) {
+    if (!containsTag(tagType)) {
+      setLong(tagType, value);
+    }
+  }
+
+  private void setBooleanIfEmpty(int tagType, boolean value) {
+    if (!containsTag(tagType)) {
+      setBoolean(tagType, value);
+    }
+  }
+
+  private void setDoubleIfEmpty(int tagType, double value) {
+    if (!containsTag(tagType)) {
+      setDouble(tagType, value);
+    }
+  }
+
+  private void setStringIfEmpty(int tagType, @NotNull String value) {
+    if (!containsTag(tagType)) {
+      setString(tagType, value);
+    }
+  }
+
+  public void setPixelWidth(int pixelWidth) {
+    setIntIfEmpty(TAG_PIXEL_WIDTH, pixelWidth);
+  }
+
+  public void setPixelWidth(Directory directory, int tagType) {
+    try {
+      setPixelWidth(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setPixelHeight(int pixelHeight) {
+    setIntIfEmpty(TAG_PIXEL_HEIGHT, pixelHeight);
+  }
+
+  public void setPixelHeight(Directory directory, int tagType) {
+    try {
+      setPixelHeight(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setOrientation(int orientation) {
+    setIntIfEmpty(TAG_ORIENTATION, orientation);
+  }
+
+  public void setOrientation(Directory directory, int tagType) {
+    try {
+      setOrientation(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setDPIWidth(double dpiWidth) {
+    setDoubleIfEmpty(TAG_DPI_WIDTH, dpiWidth);
+  }
+
+  public void setDPIWidth(Directory directory, int tagType) {
+    try {
+      setDPIWidth(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setDPIWidth(Directory directory, int tagType, double factor) {
+    try {
+      setDPIWidth(directory.getInt(tagType) * factor);
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setDPIHeight(double dpiHeight) {
+    setDoubleIfEmpty(TAG_DPI_HEIGHT, dpiHeight);
+  }
+
+  public void setDPIHeight(Directory directory, int tagType) {
+    try {
+      setDPIHeight(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setDPIHeight(Directory directory, int tagType, double factor) {
+    try {
+      setDPIHeight(directory.getInt(tagType) * factor);
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setColorMode(String colorMode) {
+    setStringIfEmpty(TAG_COLOR_MODE, colorMode);
+  }
+
+  public void setColorMode(Directory directory, int tagType) {
+    String colorMode = directory.getDescription(tagType);
+    if (colorMode != null) {
+      setColorMode(colorMode);
+    }
+  }
+
+  public void setBitPerPixel(int bitPerPixel) {
+    setIntIfEmpty(TAG_BITS_PER_PIXEL, bitPerPixel);
+  }
+
+  public void setBitPerPixel(Directory directory, int tagType) {
+    try {
+      setBitPerPixel(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setBitPerPixel(Directory directory, int tagType1, int tagType2) {
+    try {
+      setBitPerPixel(directory.getInt(tagType1) * directory.getInt(tagType2));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setAlpha(boolean alpha) {
+    setBooleanIfEmpty(TAG_HAS_ALPHA, alpha);
+  }
+
+  public void setAlpha(Directory directory, int tagType) {
+    try {
+      setAlpha(directory.getBoolean(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setDuration(long duration) {
+    setLongIfEmpty(TAG_DURATION, duration);
+  }
+
+  public void setDuration(Directory directory, int tagType) {
+    Object o = directory.getObject(tagType);
+    if (o != null) {
+      if (o instanceof String) {
+        String[] time = ((String) o).split(":");
+        setDuration(
+          Long.parseLong(time[0]) * 3600 +
+          Long.parseLong(time[1]) * 60 +
+          Long.parseLong(time[2]));
+      } else if (o instanceof Number) {
+        setDuration(((Number) o).longValue());
+      }
+    }
+  }
+
+  public void setVideoCodec(String videoCodec) {
+    setStringIfEmpty(TAG_VIDEO_CODEC, videoCodec);
+  }
+
+  public void setVideoCodec(Directory directory, int tagType) {
+    String videoCodec = directory.getString(tagType);
+    if (videoCodec != null) {
+      setVideoCodec(videoCodec);
+    }
+  }
+
+  public void setFrameRate(double frameRate) {
+    setDoubleIfEmpty(TAG_FRAME_RATE, frameRate);
+  }
+
+  public void setFrameRate(Directory directory, int tagType) {
+    try {
+      setFrameRate(directory.getDouble(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setAudioCodec(String audioCodec) {
+    setStringIfEmpty(TAG_AUDIO_CODEC, audioCodec);
+  }
+
+  public void setAudioCodec(Directory directory, int tagType) {
+    String audioCodec = directory.getString(tagType);
+    if (audioCodec != null) {
+      setAudioCodec(audioCodec);
+    }
+  }
+
+  public void setAudioSampleSize(int audioSampleSize) {
+    setIntIfEmpty(TAG_AUDIO_SAMPLE_SIZE, audioSampleSize);
+  }
+
+  public void setAudioSampleSize(Directory directory, int tagType) {
+    try {
+      setAudioSampleSize(directory.getInt(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+
+  public void setAudioSampleRate(double audioSampleRate) {
+    setDoubleIfEmpty(TAG_AUDIO_SAMPLE_RATE, audioSampleRate);
+  }
+
+  public void setAudioSampleRate(Directory directory, int tagType) {
+    try {
+      setAudioSampleRate(directory.getDouble(tagType));
+    } catch (MetadataException e) {
+      // Nothing needs to be done
+    }
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataReader.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataReader.java
new file mode 100644
index 0000000..cec677d
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/GenericMetadataReader.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import com.drew.imaging.FileType;
+import com.drew.imaging.png.PngChunkType;
+import com.drew.imaging.png.PngColorType;
+import com.drew.lang.annotations.NotNull;
+import com.drew.metadata.Directory;
+import com.drew.metadata.Metadata;
+import com.drew.metadata.MetadataException;
+import com.drew.metadata.avi.AviDirectory;
+import com.drew.metadata.bmp.BmpHeaderDirectory;
+import com.drew.metadata.eps.EpsDirectory;
+import com.drew.metadata.exif.ExifIFD0Directory;
+import com.drew.metadata.exif.ExifSubIFDDirectory;
+import com.drew.metadata.gif.GifControlDirectory;
+import com.drew.metadata.gif.GifHeaderDirectory;
+import com.drew.metadata.ico.IcoDirectory;
+import com.drew.metadata.jfif.JfifDirectory;
+import com.drew.metadata.jpeg.JpegDirectory;
+import com.drew.metadata.mov.QuickTimeDirectory;
+import com.drew.metadata.mov.media.QuickTimeSoundDirectory;
+import com.drew.metadata.mov.media.QuickTimeVideoDirectory;
+import com.drew.metadata.mp4.Mp4Directory;
+import com.drew.metadata.mp4.media.Mp4SoundDirectory;
+import com.drew.metadata.mp4.media.Mp4VideoDirectory;
+import com.drew.metadata.pcx.PcxDirectory;
+import com.drew.metadata.photoshop.PsdHeaderDirectory;
+import com.drew.metadata.png.PngDirectory;
+import com.drew.metadata.wav.WavDirectory;
+import com.drew.metadata.webp.WebpDirectory;
+import org.apache.hadoop.fs.FileStatus;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+public class GenericMetadataReader
+{
+  public void read(@NotNull FileType fileType, @NotNull FileStatus fileStatus, @NotNull Metadata metadata)
+  {
+    GenericMetadataDirectory directory = new GenericMetadataDirectory();
+    boolean skipEPSPreview = false;
+
+    directory.setLong(GenericMetadataDirectory.TAG_FILE_SIZE, fileStatus.getLen());
+    // Add local time zone offset to store the last modified time as local time
+    // just like TO_TIMESTAMP(UNIX_TIMESTAMP()) returns local time
+    directory.setDate(GenericMetadataDirectory.TAG_FILE_DATE_TIME,
+        new Date(fileStatus.getModificationTime() + TimeZone.getDefault().getRawOffset()));
+    directory.setString(GenericMetadataDirectory.TAG_FORMAT, fileType.name().toUpperCase());
+
+    for (Directory dir : metadata.getDirectories()) {
+
+      if (dir instanceof JpegDirectory) {
+        final JpegDirectory jpegDir = (JpegDirectory)dir;
+        directory.setPixelWidth(jpegDir, JpegDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(jpegDir, JpegDirectory.TAG_IMAGE_HEIGHT);
+        directory.setBitPerPixel(jpegDir, JpegDirectory.TAG_DATA_PRECISION, JpegDirectory.TAG_NUMBER_OF_COMPONENTS);
+        continue;
+      }
+
+      if (dir instanceof JfifDirectory) {
+        final JfifDirectory jfifDir = (JfifDirectory)dir;
+        try {
+          final int unit = jfifDir.getResUnits();
+          if (unit == 1 || unit == 2) {
+            directory.setDPIWidth(jfifDir, JfifDirectory.TAG_RESX, unit == 1 ? 1.0 : 2.54);
+            directory.setDPIHeight(jfifDir, JfifDirectory.TAG_RESY, unit == 1 ? 1.0 : 2.54);
+          }
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        continue;
+      }
+
+      if (dir instanceof ExifIFD0Directory) {
+        if (skipEPSPreview) {
+          skipEPSPreview = false;
+          continue;
+        }
+
+        final ExifIFD0Directory ifd0Dir = (ExifIFD0Directory)dir;
+        directory.setPixelWidth(ifd0Dir, ExifIFD0Directory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(ifd0Dir, ExifIFD0Directory.TAG_IMAGE_HEIGHT);
+        directory.setOrientation(ifd0Dir, ExifIFD0Directory.TAG_ORIENTATION);
+        try {
+          final int unit = ifd0Dir.getInt(ExifIFD0Directory.TAG_RESOLUTION_UNIT);
+          if (unit == 2 || unit == 3) {
+            directory.setDPIWidth(ifd0Dir, ExifIFD0Directory.TAG_X_RESOLUTION, unit == 2 ? 1.0 : 2.54);
+            directory.setDPIHeight(ifd0Dir, ExifIFD0Directory.TAG_Y_RESOLUTION, unit == 2 ? 1.0 : 2.54);
+          }
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        int[] bitPerSample = ifd0Dir.getIntArray(ExifIFD0Directory.TAG_BITS_PER_SAMPLE);
+        if (bitPerSample != null) {
+          int bitsPerPixel = 0;
+          for (int n : bitPerSample) {
+            bitsPerPixel += n;
+          }
+          directory.setBitPerPixel(bitsPerPixel);
+        }
+        continue;
+      }
+
+      if (dir instanceof ExifSubIFDDirectory) {
+        final ExifSubIFDDirectory subIFDDir = (ExifSubIFDDirectory)dir;
+        directory.setPixelWidth(subIFDDir, ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH);
+        directory.setPixelHeight(subIFDDir, ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT);
+        continue;
+      }
+
+      if (dir instanceof PsdHeaderDirectory) {
+        final PsdHeaderDirectory psdDir = (PsdHeaderDirectory)dir;
+        directory.setPixelWidth(psdDir, PsdHeaderDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(psdDir, PsdHeaderDirectory.TAG_IMAGE_HEIGHT);
+        directory.setBitPerPixel(
+            psdDir, PsdHeaderDirectory.TAG_BITS_PER_CHANNEL, PsdHeaderDirectory.TAG_CHANNEL_COUNT);
+        directory.setColorMode(psdDir, PsdHeaderDirectory.TAG_COLOR_MODE);
+        continue;
+      }
+
+      if (dir instanceof PngDirectory) {
+        final PngDirectory pngDir = (PngDirectory)dir;
+
+        if (pngDir.getPngChunkType() == PngChunkType.IHDR) {
+          directory.setPixelWidth(pngDir, PngDirectory.TAG_IMAGE_WIDTH);
+          directory.setPixelHeight(pngDir, PngDirectory.TAG_IMAGE_HEIGHT);
+          try {
+            int numOfComponent = 1;
+            int colorType = pngDir.getInt(PngDirectory.TAG_COLOR_TYPE);
+            if (colorType == PngColorType.IndexedColor.getNumericValue()) {
+              directory.setColorMode("Indexed");
+            } else if (colorType == PngColorType.Greyscale.getNumericValue()) {
+              directory.setColorMode("Grayscale");
+            } else if (colorType == PngColorType.GreyscaleWithAlpha.getNumericValue()) {
+              numOfComponent = 2;
+              directory.setColorMode("Grayscale");
+              directory.setAlpha(true);
+            } else if (colorType == PngColorType.TrueColor.getNumericValue()) {
+              numOfComponent = 3;
+            } else if (colorType == PngColorType.TrueColorWithAlpha.getNumericValue()) {
+              numOfComponent = 4;
+              directory.setAlpha(true);
+            }
+            directory.setBitPerPixel(pngDir.getInt(PngDirectory.TAG_BITS_PER_SAMPLE) * numOfComponent);
+          } catch (MetadataException e) {
+            // Nothing needs to be done
+          }
+          continue;
+        }
+
+        if (pngDir.getPngChunkType() == PngChunkType.pHYs) {
+          try {
+            final int unit = pngDir.getInt(PngDirectory.TAG_UNIT_SPECIFIER);
+            if (unit == 1) {
+              directory.setDPIWidth(pngDir, PngDirectory.TAG_PIXELS_PER_UNIT_X, 0.0254);
+              directory.setDPIHeight(pngDir, PngDirectory.TAG_PIXELS_PER_UNIT_Y, 0.0254);
+            }
+          } catch (MetadataException e) {
+            // Nothing needs to be done
+          }
+          continue;
+        }
+
+        if (pngDir.getPngChunkType() == PngChunkType.tRNS) {
+          directory.setAlpha(true);
+          continue;
+        }
+
+        continue;
+      }
+
+      if (dir instanceof BmpHeaderDirectory) {
+        final BmpHeaderDirectory bmpDir = (BmpHeaderDirectory)dir;
+        directory.setPixelWidth(bmpDir, BmpHeaderDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(bmpDir, BmpHeaderDirectory.TAG_IMAGE_HEIGHT);
+        directory.setDPIWidth(bmpDir, BmpHeaderDirectory.TAG_X_PIXELS_PER_METER, 0.0254);
+        directory.setDPIHeight(bmpDir, BmpHeaderDirectory.TAG_Y_PIXELS_PER_METER, 0.0254);
+        try {
+          final int bitsPerPixel = bmpDir.getInt(BmpHeaderDirectory.TAG_BITS_PER_PIXEL);
+          if (bitsPerPixel <= 8) {
+            directory.setColorMode("Indexed");
+          }
+          directory.setBitPerPixel(bitsPerPixel);
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        continue;
+      }
+
+      if (dir instanceof GifHeaderDirectory) {
+        final GifHeaderDirectory gifDir = (GifHeaderDirectory)dir;
+        directory.setPixelWidth(gifDir, GifHeaderDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(gifDir, GifHeaderDirectory.TAG_IMAGE_HEIGHT);
+        directory.setColorMode("Indexed");
+        directory.setBitPerPixel(gifDir, GifHeaderDirectory.TAG_BITS_PER_PIXEL);
+        continue;
+      }
+
+      if (dir instanceof GifControlDirectory) {
+        final GifControlDirectory gifControlDir = (GifControlDirectory)dir;
+        directory.setAlpha(gifControlDir, GifControlDirectory.TAG_TRANSPARENT_COLOR_FLAG);
+        continue;
+      }
+
+      if (dir instanceof IcoDirectory) {
+        final IcoDirectory icoDir = (IcoDirectory)dir;
+        directory.setPixelWidth(icoDir, IcoDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(icoDir, IcoDirectory.TAG_IMAGE_HEIGHT);
+        try {
+          if (icoDir.getInt(IcoDirectory.TAG_COLOUR_PALETTE_SIZE) != 0) {
+            directory.setColorMode("Indexed");
+          }
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        directory.setBitPerPixel(icoDir, IcoDirectory.TAG_BITS_PER_PIXEL);
+        directory.setAlpha(true);
+        continue;
+      }
+
+      if (dir instanceof PcxDirectory) {
+        final PcxDirectory pcxDir = (PcxDirectory)dir;
+        try {
+          directory.setPixelWidth(pcxDir.getInt(PcxDirectory.TAG_XMAX) - pcxDir.getInt(PcxDirectory.TAG_XMIN) + 1);
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        try {
+          directory.setPixelHeight(pcxDir.getInt(PcxDirectory.TAG_YMAX) - pcxDir.getInt(PcxDirectory.TAG_YMIN) + 1);
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        directory.setDPIWidth(pcxDir, PcxDirectory.TAG_HORIZONTAL_DPI);
+        directory.setDPIHeight(pcxDir, PcxDirectory.TAG_VERTICAL_DPI);
+        directory.setBitPerPixel(pcxDir, PcxDirectory.TAG_BITS_PER_PIXEL, PcxDirectory.TAG_COLOR_PLANES);
+        try {
+          int colorPlanes = pcxDir.getInt(PcxDirectory.TAG_COLOR_PLANES);
+          if (colorPlanes == 1) {
+            if (pcxDir.getInt(PcxDirectory.TAG_PALETTE_TYPE) == 2) {
+              directory.setColorMode("Grayscale");
+            } else {
+              directory.setColorMode("Indexed");
+            }
+          }
+          directory.setAlpha(colorPlanes == 4);
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        continue;
+      }
+
+      if (dir instanceof WavDirectory) {
+        final WavDirectory wavDir = (WavDirectory)dir;
+        directory.setColorMode("N/A");
+        directory.setDuration(wavDir, WavDirectory.TAG_DURATION);
+        directory.setAudioCodec(wavDir, WavDirectory.TAG_FORMAT);
+        directory.setAudioSampleSize(wavDir, WavDirectory.TAG_BITS_PER_SAMPLE);
+        directory.setAudioSampleRate(wavDir, WavDirectory.TAG_SAMPLES_PER_SEC);
+      }
+
+      if (dir instanceof AviDirectory) {
+        final AviDirectory aviDir = (AviDirectory)dir;
+        directory.setPixelWidth(aviDir, AviDirectory.TAG_WIDTH);
+        directory.setPixelHeight(aviDir, AviDirectory.TAG_HEIGHT);
+        directory.setDuration(aviDir, AviDirectory.TAG_DURATION);
+        directory.setVideoCodec(aviDir, AviDirectory.TAG_VIDEO_CODEC);
+        directory.setFrameRate(aviDir, AviDirectory.TAG_FRAMES_PER_SECOND);
+        directory.setAudioCodec(aviDir, AviDirectory.TAG_AUDIO_CODEC);
+        directory.setAudioSampleRate(aviDir, AviDirectory.TAG_SAMPLES_PER_SECOND);
+        continue;
+      }
+
+      if (dir instanceof WebpDirectory) {
+        final WebpDirectory webpDir = (WebpDirectory)dir;
+        directory.setPixelWidth(webpDir, WebpDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(webpDir, WebpDirectory.TAG_IMAGE_HEIGHT);
+        directory.setAlpha(webpDir, WebpDirectory.TAG_HAS_ALPHA);
+        continue;
+      }
+
+      if (dir instanceof QuickTimeVideoDirectory) {
+        final QuickTimeVideoDirectory qtVideoDir = (QuickTimeVideoDirectory)dir;
+        directory.setPixelWidth(qtVideoDir, QuickTimeVideoDirectory.TAG_WIDTH);
+        directory.setPixelHeight(qtVideoDir, QuickTimeVideoDirectory.TAG_HEIGHT);
+        directory.setDPIWidth(qtVideoDir, QuickTimeVideoDirectory.TAG_HORIZONTAL_RESOLUTION);
+        directory.setDPIHeight(qtVideoDir, QuickTimeVideoDirectory.TAG_VERTICAL_RESOLUTION);
+        try {
+          int bitsPerPixel = qtVideoDir.getInt(QuickTimeVideoDirectory.TAG_DEPTH) % 32;
+          directory.setBitPerPixel(bitsPerPixel);
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        directory.setDuration(qtVideoDir, QuickTimeVideoDirectory.TAG_DURATION);
+        directory.setVideoCodec(qtVideoDir, QuickTimeVideoDirectory.TAG_COMPRESSION_TYPE);
+        directory.setFrameRate(qtVideoDir, QuickTimeVideoDirectory.TAG_FRAME_RATE);
+        continue;
+      }
+
+      if (dir instanceof QuickTimeSoundDirectory) {
+        final QuickTimeSoundDirectory qtSoundDir = (QuickTimeSoundDirectory)dir;
+        directory.setAudioCodec(qtSoundDir, QuickTimeSoundDirectory.TAG_AUDIO_FORMAT);
+        directory.setAudioSampleSize(qtSoundDir, QuickTimeSoundDirectory.TAG_AUDIO_SAMPLE_SIZE);
+        directory.setAudioSampleRate(qtSoundDir, QuickTimeSoundDirectory.TAG_AUDIO_SAMPLE_RATE);
+        continue;
+      }
+
+      if (dir instanceof QuickTimeDirectory) {
+        final QuickTimeDirectory qtDir = (QuickTimeDirectory)dir;
+        directory.setDuration(qtDir, QuickTimeDirectory.TAG_DURATION);
+        continue;
+      }
+
+     if (dir instanceof Mp4VideoDirectory) {
+        final Mp4VideoDirectory mp4VideoDir = (Mp4VideoDirectory)dir;
+        directory.setPixelWidth(mp4VideoDir, Mp4VideoDirectory.TAG_WIDTH);
+        directory.setPixelHeight(mp4VideoDir, Mp4VideoDirectory.TAG_HEIGHT);
+        directory.setDPIWidth(mp4VideoDir, Mp4VideoDirectory.TAG_HORIZONTAL_RESOLUTION);
+        directory.setDPIHeight(mp4VideoDir, Mp4VideoDirectory.TAG_VERTICAL_RESOLUTION);
+        try {
+          int bitsPerPixel = mp4VideoDir.getInt(Mp4VideoDirectory.TAG_DEPTH) % 32;
+          directory.setBitPerPixel(bitsPerPixel);
+        } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        directory.setDuration(mp4VideoDir, Mp4VideoDirectory.TAG_DURATION);
+        directory.setVideoCodec(mp4VideoDir, Mp4VideoDirectory.TAG_COMPRESSION_TYPE);
+        directory.setFrameRate(mp4VideoDir, Mp4VideoDirectory.TAG_FRAME_RATE);
+        continue;
+      }
+
+      if (dir instanceof Mp4SoundDirectory) {
+        final Mp4SoundDirectory mp4SoundDir = (Mp4SoundDirectory)dir;
+        directory.setAudioCodec(mp4SoundDir, Mp4SoundDirectory.TAG_AUDIO_FORMAT);
+        directory.setAudioSampleSize(mp4SoundDir, Mp4SoundDirectory.TAG_AUDIO_SAMPLE_SIZE);
+        directory.setAudioSampleRate(mp4SoundDir, Mp4SoundDirectory.TAG_AUDIO_SAMPLE_RATE);
+        continue;
+      }
+
+      if (dir instanceof Mp4Directory) {
+        final Mp4Directory mp4Dir = (Mp4Directory)dir;
+        directory.setDuration(mp4Dir, Mp4Directory.TAG_DURATION);
+        continue;
+      }
+
+      if (dir instanceof EpsDirectory) {
+        final EpsDirectory epsDir = (EpsDirectory)dir;
+        directory.setPixelWidth(epsDir, EpsDirectory.TAG_IMAGE_WIDTH);
+        directory.setPixelHeight(epsDir, EpsDirectory.TAG_IMAGE_HEIGHT);
+        try {
+          int bitsPerPixel = 24;
+          int colorType = epsDir.getInt(EpsDirectory.TAG_COLOR_TYPE);
+          if (colorType == 1) {
+            String imageData = epsDir.getString(EpsDirectory.TAG_IMAGE_DATA);
+            if (imageData != null && imageData.split(" ")[2].equals("1")) {
+              bitsPerPixel = 1;
+              directory.setColorMode("Bitmap");
+            } else {
+              bitsPerPixel = 8;
+              directory.setColorMode("Grayscale");
+            }
+          } else if (colorType == 2) {
+            directory.setColorMode("Lab");
+          } else if (colorType == 4) {
+            bitsPerPixel = 32;
+            directory.setColorMode("CMYK");
+          }
+          directory.setBitPerPixel(bitsPerPixel);
+          skipEPSPreview = epsDir.containsTag(EpsDirectory.TAG_TIFF_PREVIEW_SIZE);
+         } catch (MetadataException e) {
+          // Nothing needs to be done
+        }
+        continue;
+      }
+    }
+
+    // Set default value if empty
+    directory.setPixelWidth(0);
+    directory.setPixelHeight(0);
+    directory.setOrientation(0);
+    directory.setDPIWidth(0.0);
+    directory.setDPIHeight(0.0);
+    directory.setColorMode("RGB");
+    directory.setBitPerPixel(0);
+    directory.setAlpha(false);
+    directory.setDuration(0);
+    directory.setVideoCodec("Unknown");
+    directory.setFrameRate(0.0);
+    directory.setAudioCodec("Unknown");
+    directory.setAudioSampleSize(0);
+    directory.setAudioSampleRate(0.0);
+
+    metadata.addDirectory(directory);
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageFormatConfig.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageFormatConfig.java
new file mode 100644
index 0000000..84d957f
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageFormatConfig.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import java.util.List;
+
+import org.apache.drill.common.logical.FormatPluginConfig;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.google.common.collect.ImmutableList;
+
+@JsonTypeName("image") @JsonInclude(Include.NON_DEFAULT)
+public class ImageFormatConfig implements FormatPluginConfig {
+
+  public List<String> extensions = ImmutableList.of();
+  public boolean fileSystemMetadata = true;
+  public boolean descriptive = true;
+  public String timeZone = null;
+
+  public List<String> getExtensions() {
+    return extensions;
+  }
+
+  public boolean hasFileSystemMetadata() {
+    return fileSystemMetadata;
+  }
+
+  public boolean isDescriptive() {
+    return descriptive;
+  }
+
+  public String getTimeZone() {
+    return timeZone;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((extensions == null) ? 0 : extensions.hashCode());
+    result = prime * result + (fileSystemMetadata ? 1231 : 1237);
+    result = prime * result + (descriptive ? 1231 : 1237);
+    result = prime * result + ((timeZone == null) ? 0 : timeZone.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if (getClass() != obj.getClass()) {
+      return false;
+    }
+    ImageFormatConfig other = (ImageFormatConfig) obj;
+    if (extensions == null) {
+      if (other.extensions != null) {
+        return false;
+      }
+    } else if (!extensions.equals(other.extensions)) {
+      return false;
+    }
+    if (fileSystemMetadata != other.fileSystemMetadata) {
+      return false;
+    }
+    if (descriptive != other.descriptive) {
+      return false;
+    }
+    if (timeZone == null) {
+      if (other.timeZone != null) {
+        return false;
+      }
+    } else if (!timeZone.equals(other.timeZone)) {
+      return false;
+    }
+    return true;
+  }
+}
\ No newline at end of file
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageFormatPlugin.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageFormatPlugin.java
new file mode 100644
index 0000000..6b0b9b4
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageFormatPlugin.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.drill.common.exceptions.ExecutionSetupException;
+import org.apache.drill.common.expression.SchemaPath;
+import org.apache.drill.common.logical.StoragePluginConfig;
+import org.apache.drill.exec.ops.FragmentContext;
+import org.apache.drill.exec.server.DrillbitContext;
+import org.apache.drill.exec.store.RecordReader;
+import org.apache.drill.exec.store.RecordWriter;
+import org.apache.drill.exec.store.dfs.DrillFileSystem;
+import org.apache.drill.exec.store.dfs.easy.EasyFormatPlugin;
+import org.apache.drill.exec.store.dfs.easy.EasyWriter;
+import org.apache.drill.exec.store.dfs.easy.FileWork;
+import org.apache.hadoop.conf.Configuration;
+
+public class ImageFormatPlugin extends EasyFormatPlugin<ImageFormatConfig> {
+
+  private final static String DEFAULT_NAME = "image";
+
+  public ImageFormatPlugin(String name, DrillbitContext context, Configuration fsConf,
+                           StoragePluginConfig storageConfig) {
+    super(name, context, fsConf, storageConfig, new ImageFormatConfig(), true, false, false, false,
+        Collections.<String>emptyList(), DEFAULT_NAME);
+  }
+
+  public ImageFormatPlugin(String name, DrillbitContext context, Configuration fsConf,
+                           StoragePluginConfig storageConfig, ImageFormatConfig formatConfig) {
+    super(name, context, fsConf, storageConfig, formatConfig, true, false, false, false,
+        formatConfig.getExtensions(), DEFAULT_NAME);
+  }
+
+  @Override
+  public RecordReader getRecordReader(FragmentContext context, DrillFileSystem dfs, FileWork fileWork,
+      List<SchemaPath> columns, String userName) throws ExecutionSetupException {
+    return new ImageRecordReader(context, dfs, fileWork.getPath(),
+        ((ImageFormatConfig)formatConfig).hasFileSystemMetadata(),
+        ((ImageFormatConfig)formatConfig).isDescriptive(),
+        ((ImageFormatConfig)formatConfig).getTimeZone());
+  }
+
+  @Override
+  public RecordWriter getRecordWriter(FragmentContext context, EasyWriter writer) throws IOException {
+    throw new UnsupportedOperationException("Drill doesn't currently support writing to image files.");
+  }
+
+  @Override
+  public int getReaderOperatorType() {
+    return 4002;
+  }
+
+  @Override
+  public int getWriterOperatorType() {
+    throw new UnsupportedOperationException("Drill doesn't currently support writing to image files.");
+  }
+
+  @Override
+  public boolean supportsPushDown() {
+    return true;
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageRecordReader.java b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageRecordReader.java
new file mode 100644
index 0000000..91f8b99
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/store/image/ImageRecordReader.java
@@ -0,0 +1,493 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import io.netty.buffer.DrillBuf;
+
+import java.io.BufferedInputStream;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
+
+import com.adobe.xmp.XMPException;
+import com.adobe.xmp.XMPMeta;
+import com.adobe.xmp.options.IteratorOptions;
+import com.adobe.xmp.properties.XMPPropertyInfo;
+
+import com.drew.imaging.FileType;
+import com.drew.imaging.FileTypeDetector;
+import com.drew.imaging.ImageMetadataReader;
+import com.drew.imaging.ImageProcessingException;
+import com.drew.lang.Charsets;
+import com.drew.lang.KeyValuePair;
+import com.drew.lang.Rational;
+import com.drew.metadata.Directory;
+import com.drew.metadata.Metadata;
+import com.drew.metadata.StringValue;
+import com.drew.metadata.Tag;
+import com.drew.metadata.eps.EpsDirectory;
+import com.drew.metadata.exif.ExifIFD0Directory;
+import com.drew.metadata.exif.ExifInteropDirectory;
+import com.drew.metadata.exif.ExifSubIFDDirectory;
+import com.drew.metadata.exif.GpsDirectory;
+import com.drew.metadata.exif.PanasonicRawIFD0Directory;
+import com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory;
+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.SamsungType2MakernoteDirectory;
+import com.drew.metadata.exif.makernotes.SonyType6MakernoteDirectory;
+import com.drew.metadata.icc.IccDirectory;
+import com.drew.metadata.jpeg.JpegComponent;
+import com.drew.metadata.photoshop.PhotoshopDirectory;
+import com.drew.metadata.png.PngDirectory;
+import com.drew.metadata.xmp.XmpDirectory;
+
+import org.apache.drill.common.exceptions.ExecutionSetupException;
+import org.apache.drill.common.exceptions.UserException;
+import org.apache.drill.exec.ops.FragmentContext;
+import org.apache.drill.exec.ops.OperatorContext;
+import org.apache.drill.exec.physical.impl.OutputMutator;
+import org.apache.drill.exec.store.AbstractRecordReader;
+import org.apache.drill.exec.store.dfs.DrillFileSystem;
+import org.apache.drill.exec.vector.complex.impl.VectorContainerWriter;
+import org.apache.drill.exec.vector.complex.writer.BaseWriter.ListWriter;
+import org.apache.drill.exec.vector.complex.writer.BaseWriter.MapWriter;
+import org.apache.drill.exec.vector.complex.writer.FieldWriter;
+import org.apache.drill.exec.vector.complex.writer.VarCharWriter;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.Path;
+
+public class ImageRecordReader extends AbstractRecordReader {
+
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ImageRecordReader.class);
+
+  private final DrillFileSystem fs;
+  private final Path hadoopPath;
+  private final boolean fileSystemMetadata;
+  private final boolean descriptive;
+  private final TimeZone timeZone;
+
+  private VectorContainerWriter writer;
+  private FileStatus fileStatus;
+  private BufferedInputStream metadataStream;
+  private DrillBuf managedBuffer;
+  private boolean finish;
+
+  public ImageRecordReader(FragmentContext context, DrillFileSystem fs, String inputPath,
+                           boolean fileSystemMetadata, boolean descriptive, String timeZone) {
+    this.fs = fs;
+    hadoopPath = fs.makeQualified(new Path(inputPath));
+    this.fileSystemMetadata = fileSystemMetadata;
+    this.descriptive = descriptive;
+    this.timeZone = (timeZone != null) ? TimeZone.getTimeZone(timeZone) : TimeZone.getDefault();
+    managedBuffer = context.getManagedBuffer();
+  }
+
+  @Override
+  public void setup(OperatorContext context, OutputMutator output) throws ExecutionSetupException {
+
+    try {
+      fileStatus = fs.getFileStatus(hadoopPath);
+      metadataStream = new BufferedInputStream(fs.open(hadoopPath));
+      writer = new VectorContainerWriter(output);
+      finish = false;
+    } catch (Exception e) {
+      throw handleAndRaise("Failure in creating record reader", e);
+    }
+  }
+
+  private DrillBuf drillBuffer(byte[] b) {
+    if (managedBuffer.capacity() < b.length) {
+      managedBuffer = managedBuffer.reallocIfNeeded(b.length);
+    }
+    managedBuffer.clear();
+    managedBuffer.writeBytes(b);
+    return managedBuffer;
+  }
+
+  protected RuntimeException handleAndRaise(String s, Exception e) {
+    throw UserException.dataReadError(e)
+        .message(s + "\n%s", e.getMessage())
+        .addContext("Path", hadoopPath.toUri().getPath())
+        .build(logger);
+  }
+
+  @Override
+  public int next() {
+
+    if (finish) {
+      return 0;
+    }
+
+    try {
+      writer.allocate();
+      writer.reset();
+
+      final MapWriter rootWriter = writer.rootAsMap();
+      final FileType fileType = FileTypeDetector.detectFileType(metadataStream);
+      final Metadata metadata = ImageMetadataReader.readMetadata(metadataStream);
+
+      try {
+        new GenericMetadataReader().read(fileType, fileStatus, metadata);
+        processGenericMetadataDirectory(rootWriter,
+            metadata.getFirstDirectoryOfType(GenericMetadataDirectory.class));
+      } catch (Exception e) {
+        // simply skip this directory
+      }
+
+      boolean skipEPSPreview = false;
+
+      for (Directory directory : metadata.getDirectories()) {
+        try {
+          if (directory instanceof GenericMetadataDirectory) {
+            continue;
+          }
+          if (directory instanceof ExifIFD0Directory && skipEPSPreview) {
+            skipEPSPreview = false;
+            continue;
+          }
+          if (directory instanceof EpsDirectory) {
+            // If an EPS file contains a TIFF preview, skip the next IFD0
+            skipEPSPreview = directory.containsTag(EpsDirectory.TAG_TIFF_PREVIEW_SIZE);
+          }
+          final MapWriter directoryWriter = rootWriter.map(formatName(directory.getName()));
+          processDirectory(directoryWriter, directory, metadata);
+          if (directory instanceof XmpDirectory) {
+            processXmpDirectory(directoryWriter, (XmpDirectory) directory);
+          }
+        } catch (Exception e) {
+          // simply skip this directory
+        }
+      }
+
+      writer.setValueCount(1);
+      finish = true;
+      return 1;
+    } catch (ImageProcessingException e) {
+      finish = true;
+      return 0;
+    } catch (Exception e) {
+      throw handleAndRaise("Failure while reading image metadata record.", e);
+    }
+  }
+
+  private void processGenericMetadataDirectory(final MapWriter writer,
+                                               final GenericMetadataDirectory directory) {
+    for (Tag tag : directory.getTags()) {
+      try {
+        final int tagType = tag.getTagType();
+        if (tagType != GenericMetadataDirectory.TAG_FILE_SIZE &&
+            tagType != GenericMetadataDirectory.TAG_FILE_DATE_TIME || fileSystemMetadata) {
+          writeValue(writer, formatName(tag.getTagName()),
+              descriptive ? directory.getDescription(tagType) : directory.getObject(tagType));
+        }
+      } catch (Exception e) {
+        // simply skip this tag
+      }
+    }
+  }
+
+  private void processDirectory(final MapWriter writer, final Directory directory, final Metadata metadata) {
+    for (Tag tag : directory.getTags()) {
+      try {
+        final int tagType = tag.getTagType();
+        Object value;
+        if (descriptive || isDescriptionTag(directory, tagType)) {
+          value = directory.getDescription(tagType);
+          if (directory instanceof PngDirectory) {
+            if (((PngDirectory) directory).getPngChunkType().areMultipleAllowed()) {
+              value = new String[] { (String) value };
+            }
+          }
+        } else {
+          value = directory.getObject(tagType);
+          if (directory instanceof ExifIFD0Directory && tagType == ExifIFD0Directory.TAG_DATETIME) {
+            ExifSubIFDDirectory exifSubIFDDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
+            String subsecond = null;
+            if (exifSubIFDDir != null) {
+              subsecond = exifSubIFDDir.getString(ExifSubIFDDirectory.TAG_SUBSECOND_TIME);
+            }
+            value = directory.getDate(tagType, subsecond, timeZone);
+          } else if (directory instanceof ExifSubIFDDirectory) {
+            if (tagType == ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL) {
+              value = ((ExifSubIFDDirectory) directory).getDateOriginal(timeZone);
+            } else if (tagType == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED) {
+              value = ((ExifSubIFDDirectory) directory).getDateDigitized(timeZone);
+            }
+          } else if (directory instanceof GpsDirectory) {
+            if (tagType == GpsDirectory.TAG_LATITUDE) {
+              value = ((GpsDirectory) directory).getGeoLocation().getLatitude();
+            } else if (tagType == GpsDirectory.TAG_LONGITUDE) {
+              value = ((GpsDirectory) directory).getGeoLocation().getLongitude();
+            }
+          }
+          if (isVersionTag(directory, tagType)) {
+            value = directory.getString(tagType, "US-ASCII");
+          } else if (isDateTag(directory, tagType)) {
+            value = directory.getDate(tagType, timeZone);
+          }
+        }
+        writeValue(writer, formatName(tag.getTagName()), value);
+      } catch (Exception e) {
+        // simply skip this tag
+      }
+    }
+  }
+
+  private void processXmpDirectory(final MapWriter writer, final XmpDirectory directory) {
+    HashSet<String> listItems = new HashSet();
+    XMPMeta xmpMeta = directory.getXMPMeta();
+    if (xmpMeta != null) {
+      try {
+        IteratorOptions iteratorOptions = new IteratorOptions().setJustLeafnodes(true);
+        for (final Iterator i = xmpMeta.iterator(iteratorOptions); i.hasNext(); ) {
+          try {
+            XMPPropertyInfo prop = (XMPPropertyInfo) i.next();
+            String path = prop.getPath();
+            String value = prop.getValue();
+            if (path != null && value != null) {
+              // handling lang-alt array items
+              if (prop.getOptions().getHasLanguage()) {
+                XMPPropertyInfo langProp = (XMPPropertyInfo) i.next();
+                if (langProp.getPath().endsWith("/xml:lang")) {
+                  String lang = langProp.getValue();
+                  path = path.replaceFirst("\\[\\d+\\]$", "") +
+                      (lang.equals("x-default") ? "" : "_" + lang);
+                }
+              }
+
+              FieldWriter writerSub = (FieldWriter) writer;
+              String[] elements = path.replaceAll("/\\w+:", "/").split(":|/|(?=\\[)");
+              for (int j = 1; j < elements.length; j++) {
+                String parent = elements[j - 1];
+                boolean isList = elements[j].startsWith("[");
+                if (parent.startsWith("[")) {
+                  writerSub = (FieldWriter) (isList ? writerSub.list() : writerSub.map());
+                  if (listItems.add(path.replaceFirst("[^\\]]+$", ""))) {
+                    writerSub.start();
+                  }
+                } else {
+                  writerSub = (FieldWriter)
+                      (isList ? writerSub.list(formatName(parent)) : writerSub.map(formatName(parent)));
+                }
+              }
+              String parent = elements[elements.length - 1];
+              VarCharWriter varCharWriter = parent.startsWith("[") ?
+                  writerSub.varChar() : writerSub.varChar(formatName(parent));
+              writeString(varCharWriter, value);
+            }
+          } catch (Exception e) {
+            // simply skip this property
+          }
+        }
+      } catch (XMPException ignored) {
+      }
+    }
+  }
+
+  private void writeValue(final MapWriter writer, final String tagName, final Object value) {
+    if (value == null) {
+      return;
+    }
+
+    if (value instanceof Boolean) {
+      writer.bit(tagName).writeBit((Boolean) value ? 1 : 0);
+    } else if (value instanceof Byte) {
+      // TINYINT is not supported
+      writer.integer(tagName).writeInt(((Byte) value).intValue());
+    } else if (value instanceof Short) {
+      // SMALLINT is not supported
+      writer.integer(tagName).writeInt(((Short) value).intValue());
+    } else if (value instanceof Integer) {
+      writer.integer(tagName).writeInt((Integer) value);
+    } else if (value instanceof Long) {
+      writer.bigInt(tagName).writeBigInt((Long) value);
+    } else if (value instanceof Float) {
+      writer.float4(tagName).writeFloat4((Float) value);
+    } else if (value instanceof Double) {
+      writer.float8(tagName).writeFloat8((Double) value);
+    } else if (value instanceof Rational) {
+      writer.float8(tagName).writeFloat8(((Rational) value).doubleValue());
+    } else if (value instanceof String) {
+      writeString(writer.varChar(tagName), (String) value);
+    } else if (value instanceof StringValue) {
+      writeString(writer.varChar(tagName), ((StringValue) value).toString());
+    } else if (value instanceof Date) {
+      writer.timeStamp(tagName).writeTimeStamp(((Date) value).getTime());
+    } else if (value instanceof boolean[]) {
+      for (boolean v : (boolean[]) value) {
+        writer.list(tagName).bit().writeBit(v ? 1 : 0);
+      }
+    } else if (value instanceof byte[]) {
+      final byte[] bytes = (byte[]) value;
+      if (bytes.length == 1) {
+        writer.integer(tagName).writeInt(bytes[0]);
+      } else if (bytes.length <= 4) {
+        ListWriter listWriter = writer.list(tagName);
+        for (byte v : bytes) {
+          listWriter.integer().writeInt(v);
+        }
+      } else {
+        writer.varBinary(tagName).writeVarBinary(0, bytes.length, drillBuffer(bytes));
+      }
+    } else if (value instanceof short[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (short v : (short[]) value) {
+        // SMALLINT is not supported
+        listWriter.integer().writeInt(v);
+      }
+    } else if (value instanceof int[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (int v : (int[]) value) {
+        listWriter.integer().writeInt(v);
+      }
+    } else if (value instanceof long[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (long v : (long[]) value) {
+        listWriter.bigInt().writeBigInt(v);
+      }
+    } else if (value instanceof float[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (float v : (float[]) value) {
+        listWriter.float4().writeFloat4(v);
+      }
+    } else if (value instanceof double[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (double v : (double[]) value) {
+        listWriter.float8().writeFloat8(v);
+      }
+    } else if (value instanceof Rational[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (Rational v : (Rational[]) value) {
+        listWriter.float8().writeFloat8(v.doubleValue());
+      }
+    } else if (value instanceof String[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (String v : (String[]) value) {
+        writeString(listWriter.varChar(), v);
+      }
+    } else if (value instanceof StringValue[]) {
+      ListWriter listWriter = writer.list(tagName);
+      for (StringValue v : (StringValue[]) value) {
+        writeString(listWriter.varChar(), v.toString());
+      }
+    } else if (value instanceof JpegComponent) {
+      final JpegComponent v = (JpegComponent) value;
+      writer.map(tagName).integer("ComponentId").writeInt(v.getComponentId());
+      writer.map(tagName).integer("HorizontalSamplingFactor").writeInt(v.getHorizontalSamplingFactor());
+      writer.map(tagName).integer("VerticalSamplingFactor").writeInt(v.getVerticalSamplingFactor());
+      writer.map(tagName).integer("QuantizationTableNumber").writeInt(v.getQuantizationTableNumber());
+    } else if (value instanceof List<?>) {
+      ListWriter listWriter = writer.list(tagName);
+      for (Object v : (List<?>) value) {
+        if (v instanceof KeyValuePair) {
+          listWriter.map().start();
+          writeString(listWriter.map().varChar("Key"), ((KeyValuePair) v).getKey());
+          writeString(listWriter.map().varChar("Value"), ((KeyValuePair) v).getValue().toString());
+          listWriter.map().end();
+        } else {
+          writeString(listWriter.varChar(), v.toString());
+        }
+      }
+    } else {
+      writeString(writer.varChar(tagName), value.toString());
+    }
+  }
+
+  private void writeString(final VarCharWriter writer, final String value) {
+    final byte[] stringBytes = value.getBytes(Charsets.UTF_8);
+    writer.writeVarChar(0, stringBytes.length, drillBuffer(stringBytes));
+  }
+
+  private String formatName(final String tagName) {
+    StringBuilder builder = new StringBuilder();
+    boolean upperCase = true;
+    for (char c : tagName.toCharArray()) {
+      if (c == ' ' || c == '-' || c == '/') {
+        upperCase = true;
+      } else {
+        builder.append(upperCase ? Character.toUpperCase(c) : c);
+        upperCase = false;
+      }
+    }
+    return builder.toString();
+  }
+
+  private boolean isDescriptionTag(final Directory directory, final int tagType) {
+    return directory instanceof IccDirectory && tagType > 0x20202020 && tagType < 0x7a7a7a7a ||
+        directory instanceof PhotoshopDirectory;
+  }
+
+  private boolean isVersionTag(final Directory directory, final int tagType) {
+    return directory instanceof ExifSubIFDDirectory &&
+        (tagType == ExifSubIFDDirectory.TAG_EXIF_VERSION || tagType == ExifSubIFDDirectory.TAG_FLASHPIX_VERSION) ||
+        directory instanceof ExifInteropDirectory &&
+        tagType == ExifInteropDirectory.TAG_INTEROP_VERSION ||
+        directory instanceof FujifilmMakernoteDirectory &&
+        tagType == FujifilmMakernoteDirectory.TAG_MAKERNOTE_VERSION ||
+        directory instanceof NikonType2MakernoteDirectory &&
+        tagType == NikonType2MakernoteDirectory.TAG_FIRMWARE_VERSION ||
+        directory instanceof OlympusCameraSettingsMakernoteDirectory &&
+        tagType == OlympusCameraSettingsMakernoteDirectory.TagCameraSettingsVersion ||
+        directory instanceof OlympusEquipmentMakernoteDirectory &&
+        tagType == OlympusEquipmentMakernoteDirectory.TAG_EQUIPMENT_VERSION ||
+        directory instanceof OlympusFocusInfoMakernoteDirectory &&
+        tagType == OlympusFocusInfoMakernoteDirectory.TagFocusInfoVersion ||
+        directory instanceof OlympusImageProcessingMakernoteDirectory &&
+        tagType == OlympusImageProcessingMakernoteDirectory.TagImageProcessingVersion ||
+        directory instanceof OlympusMakernoteDirectory &&
+        tagType == OlympusMakernoteDirectory.TAG_MAKERNOTE_VERSION ||
+        directory instanceof OlympusRawDevelopment2MakernoteDirectory &&
+        tagType == OlympusRawDevelopment2MakernoteDirectory.TagRawDevVersion ||
+        directory instanceof OlympusRawDevelopmentMakernoteDirectory &&
+        tagType == OlympusRawDevelopmentMakernoteDirectory.TagRawDevVersion ||
+        directory instanceof OlympusRawInfoMakernoteDirectory &&
+        tagType == OlympusRawInfoMakernoteDirectory.TagRawInfoVersion ||
+        directory instanceof PanasonicMakernoteDirectory &&
+        (tagType == PanasonicMakernoteDirectory.TAG_FIRMWARE_VERSION || tagType == PanasonicMakernoteDirectory.TAG_MAKERNOTE_VERSION || tagType == PanasonicMakernoteDirectory.TAG_EXIF_VERSION) ||
+        directory instanceof SamsungType2MakernoteDirectory &&
+        tagType == SamsungType2MakernoteDirectory.TagMakerNoteVersion ||
+        directory instanceof SonyType6MakernoteDirectory &&
+        tagType == SonyType6MakernoteDirectory.TAG_MAKERNOTE_THUMB_VERSION ||
+        directory instanceof PanasonicRawIFD0Directory &&
+        tagType == PanasonicRawIFD0Directory.TagPanasonicRawVersion;
+  }
+
+  private boolean isDateTag(final Directory directory, final int tagType) {
+    return directory instanceof IccDirectory && tagType == IccDirectory.TAG_PROFILE_DATETIME ||
+        directory instanceof PngDirectory && tagType == PngDirectory.TAG_LAST_MODIFICATION_TIME;
+  }
+
+  @Override
+  public void close() throws Exception {
+    if (metadataStream != null) {
+      metadataStream.close();
+    }
+  }
+}
diff --git a/exec/java-exec/src/main/resources/bootstrap-storage-plugins.json b/exec/java-exec/src/main/resources/bootstrap-storage-plugins.json
index 0b6add0..417635a 100644
--- a/exec/java-exec/src/main/resources/bootstrap-storage-plugins.json
+++ b/exec/java-exec/src/main/resources/bootstrap-storage-plugins.json
@@ -58,6 +58,15 @@
           extensions: [ "csvh" ],
           delimiter: ",",
           extractHeader: true
+        },
+        "image" : {
+          type: "image",
+          extensions: [
+            "jpg", "jpeg", "jpe", "tif", "tiff", "dng", "psd", "png", "bmp", "gif",
+            "ico", "pcx", "wav", "wave", "avi", "webp", "mov", "mp4", "m4a", "m4p",
+            "m4b", "m4r", "m4v", "3gp", "3g2", "eps", "epsf", "epsi", "ai", "arw",
+            "crw", "cr2", "nef", "orf", "raf", "rw2", "rwl", "srw", "x3f"
+          ]
         }
       }
     },
@@ -147,6 +156,15 @@
           extensions: [ "csvh" ],
           delimiter: ",",
           extractHeader: true
+        },
+        "image" : {
+          type: "image",
+          extensions: [
+            "jpg", "jpeg", "jpe", "tif", "tiff", "dng", "psd", "png", "bmp", "gif",
+            "ico", "pcx", "wav", "wave", "avi", "webp", "mov", "mp4", "m4a", "m4p",
+            "m4b", "m4r", "m4v", "3gp", "3g2", "eps", "epsf", "epsi", "ai", "arw",
+            "crw", "cr2", "nef", "orf", "raf", "rw2", "rwl", "srw", "x3f"
+          ]
         }
       }
     }
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFormatPluginOptionExtractor.java b/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFormatPluginOptionExtractor.java
index 3ac675b..8b73b53 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFormatPluginOptionExtractor.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/store/dfs/TestFormatPluginOptionExtractor.java
@@ -26,6 +26,7 @@ import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.common.scanner.RunTimeScan;
 import org.apache.drill.common.scanner.persistence.ScanResult;
 import org.apache.drill.exec.store.easy.text.TextFormatPlugin.TextFormatConfig;
+import org.apache.drill.exec.store.image.ImageFormatConfig;
 import org.junit.Test;
 
 import com.fasterxml.jackson.annotation.JsonTypeName;
@@ -65,6 +66,12 @@ public class TestFormatPluginOptionExtractor {
         case "httpd":
           assertEquals("(type: String, logFormat: String, timestampFormat: String)", d.presentParams());
           break;
+        case "image":
+          assertEquals(ImageFormatConfig.class, d.pluginConfigClass);
+          assertEquals(
+              "(type: String, fileSystemMetadata: boolean, descriptive: boolean, timeZone: String)", d.presentParams()
+          );
+          break;
         default:
           fail("add validation for format plugin type " + d.typeName);
       }
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/store/image/TestImageRecordReader.java b/exec/java-exec/src/test/java/org/apache/drill/exec/store/image/TestImageRecordReader.java
new file mode 100644
index 0000000..e5d513b
--- /dev/null
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/store/image/TestImageRecordReader.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.drill.exec.store.image;
+
+import java.util.TimeZone;
+
+import org.apache.drill.test.BaseTestQuery;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestImageRecordReader extends BaseTestQuery {
+
+  private static TimeZone defaultTimeZone;
+
+  @BeforeClass
+  public static void setUp() {
+    defaultTimeZone = TimeZone.getDefault();
+    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+  }
+
+  private void createAndQuery(String tableName, String imageFile) throws Exception {
+    final String query = String.format(
+      "select * from table(cp.`store/image/%s`(type => 'image', fileSystemMetadata => false))",
+      imageFile);
+
+    runSQL("alter session set `store.format`='json'");
+    test("create table dfs.tmp.`%s` as %s", tableName, query);
+
+    testBuilder()
+      .sqlQuery("select * from dfs.tmp.`%s`", tableName)
+      .ordered()
+      .jsonBaselineFile("store/image/" + tableName + ".json")
+      .go();
+    runSQL("alter session set `store.format` = 'parquet'");
+  }
+
+  @Test
+  public void testBmpImage() throws Exception {
+    createAndQuery("bmp", "rose-128x174-24bit.bmp");
+  }
+
+  @Test
+  public void testGifImage() throws Exception {
+    createAndQuery("gif", "rose-128x174-8bit-alpha.gif");
+  }
+
+  @Test
+  public void testIcoImage() throws Exception {
+    createAndQuery("ico", "rose-32x32-32bit-alpha.ico");
+  }
+
+  @Test
+  public void testJpegImage() throws Exception {
+    createAndQuery("jpeg", "withExifAndIptc.jpg");
+  }
+
+  @Test
+  public void testPcxImage() throws Exception {
+    createAndQuery("pcx", "rose-128x174-24bit.pcx");
+  }
+
+  @Test
+  public void testPngImage() throws Exception {
+    createAndQuery("png", "rose-128x174-32bit-alpha.png");
+  }
+
+  @Test
+  public void testPsdImage() throws Exception {
+    createAndQuery("psd", "rose-128x174-32bit-alpha.psd");
+  }
+
+  @Test
+  public void testTiffImage() throws Exception {
+    createAndQuery("tiff", "rose-128x174-24bit-lzw.tiff");
+  }
+
+  @Test
+  public void testWavImage() throws Exception {
+    createAndQuery("wav", "sample.wav");
+  }
+
+  @Test
+  public void testAviImage() throws Exception {
+    createAndQuery("avi", "sample.avi");
+  }
+
+  @Test
+  public void testWebpImage() throws Exception {
+    createAndQuery("webp", "1_webp_a.webp");
+  }
+
+  @Test
+  public void testMovImage() throws Exception {
+    createAndQuery("mov", "sample.mov");
+  }
+
+  @Test
+  public void testMp4Image() throws Exception {
+    createAndQuery("mp4", "sample.mp4");
+  }
+
+  @Test
+  public void testEpsImage() throws Exception {
+    createAndQuery("eps", "adobeJpeg1.eps");
+  }
+
+  @AfterClass
+  public static void cleanUp() {
+    TimeZone.setDefault(defaultTimeZone);
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/1_webp_a.webp b/exec/java-exec/src/test/resources/store/image/1_webp_a.webp
new file mode 100644
index 0000000..f7dc208
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/1_webp_a.webp differ
diff --git a/exec/java-exec/src/test/resources/store/image/adobeJpeg1.eps b/exec/java-exec/src/test/resources/store/image/adobeJpeg1.eps
new file mode 100644
index 0000000..b3941d6
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/adobeJpeg1.eps differ
diff --git a/exec/java-exec/src/test/resources/store/image/avi.json b/exec/java-exec/src/test/resources/store/image/avi.json
new file mode 100644
index 0000000..f97d2db
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/avi.json
@@ -0,0 +1,32 @@
+{
+  "Format" : "AVI",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "0",
+  "DPIHeight" : "0",
+  "PixelWidth" : "320",
+  "PixelHeight" : "240",
+  "BitsPerPixel" : "0",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:06",
+  "VideoCodec" : "XVID",
+  "FrameRate" : "25",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "38.281",
+  "FileType" : {
+    "DetectedFileTypeName" : "AVI",
+    "DetectedFileTypeLongName" : "Audio Video Interleaved",
+    "DetectedMIMEType" : "video/vnd.avi",
+    "ExpectedFileNameExtension" : "avi"
+  },
+  "AVI" : {
+    "Width" : "320 pixels",
+    "Height" : "240 pixels",
+    "StreamCount" : "2",
+    "FramesPerSecond" : "25",
+    "Duration" : "00:00:06",
+    "VideoCodec" : "XVID",
+    "SamplesPerSecond" : "38.281"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/bmp.json b/exec/java-exec/src/test/resources/store/image/bmp.json
new file mode 100644
index 0000000..da9d2e2
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/bmp.json
@@ -0,0 +1,36 @@
+{
+  "Format" : "BMP",
+  "PixelWidth" : "128",
+  "PixelHeight" : "174",
+  "DPIWidth" : "71.984",
+  "DPIHeight" : "71.984",
+  "BitsPerPixel" : "24",
+  "Orientaion" : "Unknown (0)",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "BMP",
+    "DetectedFileTypeLongName" : "Device Independent Bitmap",
+    "DetectedMIMEType" : "image/bmp",
+    "ExpectedFileNameExtension" : "bmp"
+  },
+  "BMPHeader" : {
+    "BitmapType" : "Standard",
+    "HeaderSize" : "40",
+    "ImageWidth" : "128",
+    "ImageHeight" : "174",
+    "Planes" : "1",
+    "BitsPerPixel" : "24",
+    "Compression" : "None",
+    "XPixelsPerMeter" : "2834",
+    "YPixelsPerMeter" : "2834",
+    "PaletteColourCount" : "0",
+    "ImportantColourCount" : "0"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/eps.json b/exec/java-exec/src/test/resources/store/image/eps.json
new file mode 100644
index 0000000..08d2268
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/eps.json
@@ -0,0 +1,116 @@
+{
+  "Format" : "EPS",
+  "Orientaion" : "Top, left side (Horizontal / normal)",
+  "DPIWidth" : "101",
+  "DPIHeight" : "101",
+  "PixelWidth" : "275",
+  "PixelHeight" : "207",
+  "BitsPerPixel" : "24",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "EPS",
+    "DetectedFileTypeLongName" : "Encapsulated PostScript",
+    "DetectedMIMEType" : "application/postscript",
+    "ExpectedFileNameExtension" : "eps"
+  },
+  "ExifIFD0" : {
+    "ImageWidth" : "275 pixels",
+    "ImageHeight" : "207 pixels",
+    "BitsPerSample" : "8 8 8 bits/component/pixel",
+    "PhotometricInterpretation" : "RGB",
+    "Orientation" : "Top, left side (Horizontal / normal)",
+    "SamplesPerPixel" : "3 samples/pixel",
+    "XResolution" : "101 dots per inch",
+    "YResolution" : "101 dots per inch",
+    "ResolutionUnit" : "Inch",
+    "Software" : "Adobe Photoshop CC 2017 (Macintosh)",
+    "DateTime" : "2017:08:16 12:24:54",
+    "Copyright" : "1999 Lars Borg"
+  },
+  "ExifSubIFD" : {
+    "ExifVersion" : "2.21",
+    "ColorSpace" : "Undefined",
+    "ExifImageWidth" : "275 pixels",
+    "ExifImageHeight" : "207 pixels"
+  },
+  "ExifThumbnail" : {
+    "Compression" : "JPEG (old-style)",
+    "XResolution" : "72 dots per inch",
+    "YResolution" : "72 dots per inch",
+    "ResolutionUnit" : "Inch",
+    "ThumbnailOffset" : "414 bytes",
+    "ThumbnailLength" : "0 bytes"
+  },
+  "ICCProfile" : {
+    "ProfileSize" : "532",
+    "CMMType" : "ADBE",
+    "Version" : "2.1.0",
+    "Class" : "Display Device",
+    "ColorSpace" : "RGB ",
+    "ProfileConnectionSpace" : "XYZ ",
+    "ProfileDateTime" : "1999:04:05 15:08:05",
+    "Signature" : "acsp",
+    "PrimaryPlatform" : "Apple Computer, Inc.",
+    "DeviceManufacturer" : "none",
+    "RenderingIntent" : "Media-Relative Colorimetric",
+    "XYZValues" : "0.964 1 0.825",
+    "TagCount" : "10",
+    "Copyright" : "(c) 1999 Adobe Systems Inc.",
+    "ProfileDescription" : "GBR",
+    "MediaWhitePoint" : "(0.9505, 1, 1.0891)",
+    "MediaBlackPoint" : "(0, 0, 0)",
+    "RedTRC" : "0.0085908",
+    "GreenTRC" : "0.0085908",
+    "BlueTRC" : "0.0085908",
+    "RedColorant" : "(0.3851, 0.7169, 0.0971)",
+    "GreenColorant" : "(0.1431, 0.0606, 0.7139)",
+    "BlueColorant" : "(0.436, 0.2225, 0.0139)"
+  },
+  "IPTC": {
+    "CodedCharacterSet" : "UTF-8",
+    "ApplicationRecordVersion" : "2",
+    "CopyrightNotice" : "1999 Lars Borg"
+  },
+  "Photoshop" : {
+    "CaptionDigest" : "218 119 165 163 16 30 63 186 160 177 8 58 1 54 252 149",
+    "PrintInfo2" : "[229 values]",
+    "PrintStyle" : "[557 values]",
+    "ResolutionInfo" : "101x101 DPI",
+    "PrintScale" : "Centered, Scale 1.0",
+    "GlobalAngle" : "30",
+    "GlobalAltitude" : "30",
+    "PrintFlags" : "0 0 0 0 0 0 0 0 1",
+    "CopyrightFlag" : "Yes",
+    "PrintFlagsInformation" : "0 1 0 0 0 0 0 0 0 1",
+    "ColorHalftoningInformation" : "[72 values]",
+    "ColorTransferFunctions" : "[112 values]",
+    "GridAndGuidesInformation" : "0 0 0 1 0 0 2 64 0 0 2 64 0 0 0 0",
+    "URLList" : "0",
+    "Slices" : " (0,0,207,275) 1 Slices",
+    "PixelAspectRatio" : "1.0",
+    "SeedNumber" : "1",
+    "ThumbnailData" : "JpegRGB, 159x120, Decomp 57600 bytes, 1572865 bpp, 8151 bytes",
+    "VersionInfo" : "1 (Adobe Photoshop, Adobe Photoshop CC 2017) 1",
+    "EPSOptions" : "1 1 0 0 0 0 0 0"
+  },
+  "EPS" : {
+    "TIFFPreviewSize" : "41802 bytes",
+    "TIFFPreviewOffset" : "30 bytes",
+    "Creator" : "Adobe Photoshop Version 2017.1.1 20170425.r.252 2017/04/25:23:00:00 CL 1113967",
+    "Title" : "adobeJpeg1.eps",
+    "CreationDate" : "8/16/17 12:24 PM",
+    "BoundingBox" : "0 0 196 148",
+    "ImageData" : "275 207 8 3 0 1 3 \"beginimage\"",
+    "ImageWidth" : "275 pixels",
+    "ImageHeight" : "207 pixels",
+    "ColorType" : "RGB",
+    "RamSize" : "170775"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/gif.json b/exec/java-exec/src/test/resources/store/image/gif.json
new file mode 100644
index 0000000..a05a78e
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/gif.json
@@ -0,0 +1,47 @@
+{
+  "Format" : "GIF",
+  "PixelWidth" : "128",
+  "PixelHeight" : "174",
+  "ColorMode" : "Indexed",
+  "BitsPerPixel" : "8",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "0",
+  "DPIHeight" : "0",
+  "HasAlpha" : "true",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {"DetectedFileTypeName" : "GIF",
+    "DetectedFileTypeLongName" : "Graphics Interchange Format",
+    "DetectedMIMEType" : "image/gif",
+    "ExpectedFileNameExtension" : "gif"
+  },
+  "GIFHeader" : {
+    "GIFFormatVersion" : "89a",
+    "ImageWidth" : "128",
+    "ImageHeight" : "174",
+    "ColorTableSize" : "256",
+    "IsColorTableSorted" : "false",
+    "BitsPerPixel" : "8",
+    "HasGlobalColorTable" : "true",
+    "BackgroundColorIndex" : "0"
+  },
+  "GIFControl" : {
+    "DisposalMethod" : "Not Specified",
+    "UserInputFlag" : "false",
+    "TransparentColorFlag" : "true",
+    "Delay" : "0",
+    "TransparentColorIndex" : "255"
+  },
+  "GIFImage" : {
+    "Left" : "0",
+    "Top" : "0",
+    "Width" : "128",
+    "Height" : "174",
+    "HasLocalColourTable" : "false",
+    "IsInterlaced" : "false"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/ico.json b/exec/java-exec/src/test/resources/store/image/ico.json
new file mode 100644
index 0000000..27466ad
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/ico.json
@@ -0,0 +1,33 @@
+{
+  "Format" : "ICO",
+  "PixelWidth" : "32",
+  "PixelHeight" : "32",
+  "BitsPerPixel" : "32",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "0",
+  "DPIHeight" : "0",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "true",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "ICO",
+    "DetectedFileTypeLongName" : "Windows Icon",
+    "DetectedMIMEType" : "image/x-icon",
+    "ExpectedFileNameExtension" : "ico"
+  },
+  "ICO" : {
+    "ImageType" : "Icon",
+    "ImageWidth" : "32 pixels",
+    "ImageHeight" : "32 pixels",
+    "ColourPaletteSize" : "No palette",
+    "ColourPlanes" : "1",
+    "BitsPerPixel" : "32",
+    "ImageSizeBytes" : "4264",
+    "ImageOffsetBytes" : "22"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/jpeg.json b/exec/java-exec/src/test/resources/store/image/jpeg.json
new file mode 100644
index 0000000..2bb357b
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/jpeg.json
@@ -0,0 +1,213 @@
+{
+  "Format" : "JPEG",
+  "DPIWidth" : "300",
+  "DPIHeight" : "300",
+  "PixelWidth" : "600",
+  "PixelHeight" : "400",
+  "BitsPerPixel" : "24",
+  "Orientaion" : "Top, left side (Horizontal / normal)",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "JPEG",
+    "DetectedFileTypeLongName" : "Joint Photographic Experts Group",
+    "DetectedMIMEType" : "image/jpeg",
+    "ExpectedFileNameExtension" : "jpg"
+  },
+  "JFIF" : {
+    "Version" : "1.2",
+    "ResolutionUnits" : "inch",
+    "XResolution" : "300 dots",
+    "YResolution" : "300 dots",
+    "ThumbnailWidthPixels" : "0",
+    "ThumbnailHeightPixels" : "0"
+  },
+  "ExifIFD0" : {
+    "ImageDescription" : "Communications","Make" : "FUJIFILM",
+    "Model" : "FinePixS1Pro",
+    "Orientation" : "Top, left side (Horizontal / normal)",
+    "XResolution" : "300 dots per inch",
+    "YResolution" : "300 dots per inch",
+    "ResolutionUnit" : "Inch",
+    "Software" : "Adobe Photoshop 7.0",
+    "DateTime" : "2002:07:19 13:28:10",
+    "Artist" : "Ian Britton",
+    "YCbCrPositioning" : "Datum point",
+    "ReferenceBlackWhite" : "[0,128,128] [255,255,255]",
+    "Copyright" : "ian Britton - FreeFoto.com"
+  },
+  "ExifSubIFD" : {
+    "FNumber" : "f/0.6",
+    "ExposureProgram" : "Shutter priority",
+    "ISOSpeedRatings" : "0",
+    "ExifVersion" : "2.00",
+    "DateTimeOriginal" : "2002:07:13 15:58:28",
+    "DateTimeDigitized" : "2002:07:13 15:58:28",
+    "ComponentsConfiguration" : "YCbCr",
+    "ShutterSpeedValue" : "1/724 sec",
+    "ApertureValue" : "f/16.0",
+    "BrightnessValue" : "333/1280",
+    "ExposureBiasValue" : "-1090519041/1677721600 EV",
+    "MeteringMode" : "Multi-segment",
+    "Flash" : "Flash did not fire",
+    "FocalLength" : "0 mm",
+    "FlashPixVersion" : "1.00",
+    "ColorSpace" : "sRGB",
+    "ExifImageWidth" : "2400 pixels",
+    "ExifImageHeight" : "1600 pixels",
+    "FocalPlaneXResolution" : "256/3085 inches",
+    "FocalPlaneYResolution" : "256/3085 inches",
+    "FocalPlaneResolutionUnit" : "Inches",
+    "SensingMethod" : "One-chip color area sensor",
+    "FileSource" : "Unknown (0)",
+    "SceneType" : "Unknown (0)"
+  },
+  "GPS" : {
+    "GPSVersionID" : "2.000",
+    "GPSLatitudeRef" : "N",
+    "GPSLatitude" : "54° 59' 22.8\"",
+    "GPSLongitudeRef" : "W",
+    "GPSLongitude" : "-1° 54' 51\"",
+    "GPSTimeStamp" : "14:58:24.000 UTC",
+    "GPSMapDatum" : "WGS84"
+  },
+  "ExifThumbnail" : {
+    "Compression" : "JPEG (old-style)",
+    "XResolution" : "72 dots per inch",
+    "YResolution" : "72 dots per inch",
+    "ResolutionUnit" : "Inch",
+    "ThumbnailOffset" : "1038 bytes",
+    "ThumbnailLength" : "3662 bytes"
+  },
+  "XMP" : {
+    "XMPValueCount" : "33",
+    "Photoshop" : {
+      "AuthorsPosition" : "Photographer",
+      "Urgency" : "5",
+      "SupplementalCategories" : ["Communications"],
+      "DateCreated" : "2002-06-20",
+      "Credit" : "Ian Britton",
+      "CaptionWriter" : "Ian Britton",
+      "City" : " ",
+      "Headline" : "Communications",
+      "State" : " ",
+      "Source" : "FreeFoto.com",
+      "Category" : "BUS",
+      "Country" : "Ubited Kingdom"
+    },
+    "Dc" : {
+      "Creator" : ["Ian Britton"],
+      "Description" : "Communications",
+      "Rights" : "ian Britton - FreeFoto.com",
+      "Title" : "Communications",
+      "Subject" : ["Communications"]
+    },
+    "XmpMM" : {
+      "DocumentID" : "adobe:docid:photoshop:84d4dba8-9b11-11d6-895d-c4d063a70fb0",
+      "InstanceID" : "uuid:3ff5d382-9b12-11d6-895d-c4d063a70fb0"
+    },
+    "XmpBJ" : {
+      "JobRef" : [{
+        "Name" : "Photographer"
+      }]
+    },
+    "XmpRights" : {
+      "Marked" : "True",
+      "WebStatement" : "www.freefoto.com"
+    }
+  },
+  "ICCProfile": {
+    "ProfileSize" : "3144",
+    "CMMType" : "Lino",
+    "Version" : "2.1.0",
+    "Class" : "Display Device","ColorSpace" : "RGB ",
+    "ProfileConnectionSpace" : "XYZ ",
+    "ProfileDateTime" : "1998:02:09 06:49:00",
+    "Signature" : "acsp",
+    "PrimaryPlatform" : "Microsoft Corporation",
+    "DeviceManufacturer" : "IEC ",
+    "DeviceModel" : "sRGB",
+    "XYZValues" : "0.964 1 0.825",
+    "TagCount" : "17",
+    "Copyright" : "Copyright (c) 1998 Hewlett-Packard Company",
+    "ProfileDescription" : "sRGB IEC61966-2.1",
+    "MediaWhitePoint" : "(0.9505, 1, 1.0891)",
+    "MediaBlackPoint" : "(0, 0, 0)",
+    "RedColorant" : "(0.4361, 0.2225, 0.0139)",
+    "GreenColorant" : "(0.3851, 0.7169, 0.0971)",
+    "BlueColorant" : "(0.1431, 0.0606, 0.7141)",
+    "DeviceMfgDescription" : "IEC http://www.iec.ch",
+    "DeviceModelDescription" : "IEC 61966-2.1 Default RGB colour space - sRGB",
+    "ViewingConditionsDescription" : "Reference Viewing Condition in IEC61966-2.1",
+    "ViewingConditions" : "view (0x76696577): 36 bytes",
+    "Luminance" : "(76.0365, 80, 87.1246)",
+    "Measurement" : "1931 2° Observer, Backing (0, 0, 0), Geometry Unknown, Flare 1%, Illuminant D65",
+    "Technology" : "CRT ",
+    "RedTRC" : "0.0, 0.0000763, 0.0001526, 0.0002289, 0.0003052, 0.0003815, 0.0004578, 0.0005341, 0.0006104, 0.0006867, 0.000763, 0.0008392, 0.0009003, 0.0009766, 0.0010529, 0.0011292, 0.0012055, 0.0012818, 0.0013581, 0.0014343, 0.0015106, 0.0015869, 0.0016632, 0.0017395, 0.0018158, 0.0018921, 0.0019684, 0.0020447, 0.002121, 0.0021973, 0.0022736, 0.0023499, 0.0024262, 0.0025025, 0.0025788, 0.0026551, 0.0027161, 0.0027924, 0.0028687, 0.002945, 0.0030213, 0.0030976, 0.0031739, 0.0032502, 0 [...]
+    "GreenTRC" : "0.0, 0.0000763, 0.0001526, 0.0002289, 0.0003052, 0.0003815, 0.0004578, 0.0005341, 0.0006104, 0.0006867, 0.000763, 0.0008392, 0.0009003, 0.0009766, 0.0010529, 0.0011292, 0.0012055, 0.0012818, 0.0013581, 0.0014343, 0.0015106, 0.0015869, 0.0016632, 0.0017395, 0.0018158, 0.0018921, 0.0019684, 0.0020447, 0.002121, 0.0021973, 0.0022736, 0.0023499, 0.0024262, 0.0025025, 0.0025788, 0.0026551, 0.0027161, 0.0027924, 0.0028687, 0.002945, 0.0030213, 0.0030976, 0.0031739, 0.0032502, [...]
+    "BlueTRC" : "0.0, 0.0000763, 0.0001526, 0.0002289, 0.0003052, 0.0003815, 0.0004578, 0.0005341, 0.0006104, 0.0006867, 0.000763, 0.0008392, 0.0009003, 0.0009766, 0.0010529, 0.0011292, 0.0012055, 0.0012818, 0.0013581, 0.0014343, 0.0015106, 0.0015869, 0.0016632, 0.0017395, 0.0018158, 0.0018921, 0.0019684, 0.0020447, 0.002121, 0.0021973, 0.0022736, 0.0023499, 0.0024262, 0.0025025, 0.0025788, 0.0026551, 0.0027161, 0.0027924, 0.0028687, 0.002945, 0.0030213, 0.0030976, 0.0031739, 0.0032502,  [...]
+  },
+  "JPEG" : {
+    "CompressionType" : "Baseline",
+    "DataPrecision" : "8 bits",
+    "ImageHeight" : "400 pixels",
+    "ImageWidth" : "600 pixels",
+    "NumberOfComponents" : "3",
+    "Component1" : "Y component: Quantization table 0, Sampling factors 2 horiz/2 vert",
+    "Component2" : "Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert",
+    "Component3" : "Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert"
+  },
+  "IPTC" : {
+    "ApplicationRecordVersion" : "2",
+    "CaptionAbstract" : "Communications",
+    "CaptionWriterEditor" : "Ian Britton",
+    "Headline" : "Communications",
+    "ByLine" : "Ian Britton",
+    "ByLineTitle" : "Photographer",
+    "Credit" : "Ian Britton",
+    "Source" : "FreeFoto.com",
+    "ObjectName" : "Communications",
+    "DateCreated" : "2002:06:20",
+    "City" : " ",
+    "ProvinceState" : " ",
+    "CountryPrimaryLocationName" : "Ubited Kingdom",
+    "Category" : "BUS",
+    "SupplementalCategory(s)" : "Communications",
+    "Urgency" : "53",
+    "Keywords" : "Communications",
+    "CopyrightNotice" : "ian Britton - FreeFoto.com"
+  },
+  "Photoshop" : {
+    "CaptionDigest" : "245 138 68 109 96 203 177 136 63 66 1 237 68 32 172 54",
+    "ResolutionInfo" : "300x300 DPI",
+    "PrintScale" : "Centered, Scale 1.0",
+    "GlobalAngle" : "30","GlobalAltitude" : "30",
+    "PrintFlags" : "0 0 0 0 0 0 0 0 1",
+    "CopyrightFlag" : "Yes","URL" : "www.freefoto.com",
+    "PrintFlagsInformation" : "0 1 0 0 0 0 0 0 0 2",
+    "ColorHalftoningInformation" : "[72 values]",
+    "ColorTransferFunctions" : "[112 values]",
+    "GridAndGuidesInformation" : "0 0 0 1 0 0 2 64 0 0 2 64 0 0 0 0",
+    "URLList" : "0",
+    "Slices" : "04_02_10_a5 (0,0,1600,2400) 1 Slices",
+    "SeedNumber" : "1",
+    "ThumbnailData" : "JpegRGB, 128x85, Decomp 32640 bytes, 1572865 bpp, 3662 bytes",
+    "VersionInfo" : "1 (Adobe Photoshop, Adobe Photoshop 7.0) 1",
+    "JPEGQuality" : "9 (High), Standard format, 3 scans"
+  },
+  "AdobeJPEG": {
+    "DCTEncodeVersion" : "25600",
+    "Flags0" : "64",
+    "Flags1" : "0",
+    "ColorTransform" : "YCbCr"
+  },
+  "Huffman" : {
+    "NumberOfTables" : "4 Huffman tables"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/mov.json b/exec/java-exec/src/test/resources/store/image/mov.json
new file mode 100644
index 0000000..bf174ca
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/mov.json
@@ -0,0 +1,67 @@
+{
+  "Format" : "MOV",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "72",
+  "DPIHeight" : "72",
+  "PixelWidth" : "560",
+  "PixelHeight" : "320",
+  "BitsPerPixel" : "0",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:05",
+  "VideoCodec" : "MPEG-4",
+  "FrameRate" : "0",
+  "AudioCodec" : "MPEG-4, Advanced Audio Coding (AAC)",
+  "AudioSampleSize" : "16",
+  "AudioSampleRate" : "44100",
+  "FileType" : {
+    "DetectedFileTypeName" : "MOV",
+    "DetectedFileTypeLongName" : "QuickTime Movie",
+    "DetectedMIMEType" : "video/quicktime",
+    "ExpectedFileNameExtension" : "mov"
+  },
+  "QuickTime" : {
+    "MajorBrand" : "Apple QuickTime (.MOV/QT)",
+    "MinorVersion" : "512",
+    "CompatibleBrands" : "[Apple QuickTime (.MOV/QT)]",
+    "CreationTime" : "Fri Jan 01 00:00:00 +00:00 1904",
+    "ModificationTime" : "Fri Jan 01 00:00:00 +00:00 1904",
+    "Duration" : "00:00:05",
+    "MediaTimeScale" : "1000",
+    "PreferredRate" : "1",
+    "PreferredVolume" : "1",
+    "PreviewTime" : "0",
+    "PreviewDuration" : "0",
+    "PosterTime" : "0",
+    "SelectionTime" : "0",
+    "SelectionDuration" : "0",
+    "CurrentTime" : "0",
+    "NextTrackID" : "3"
+  },
+  "QuickTimeVideo" : {
+    "CreationTime" : "Fri Jan 01 00:00:00 UTC 1904",
+    "ModificationTime" : "Fri Jan 01 00:00:00 UTC 1904",
+    "Opcolor" : "0 0 0",
+    "GraphicsMode" : "Copy",
+    "Vendor" : "FFmpeg",
+    "CompressionType" : "MPEG-4",
+    "TemporalQuality" : "512",
+    "SpatialQuality" : "512",
+    "Width" : "560 pixels",
+    "Height" : "320 pixels",
+    "CompressorName" : "mpeg4",
+    "Depth" : "Unknown (0)",
+    "ColorTable" : "Color table within file",
+    "HorizontalResolution" : "72",
+    "VerticalResolution" : "72"
+  },
+  "QuickTimeSound" : {
+    "CreationTime" : "Fri Jan 01 00:00:00 UTC 1904",
+    "ModificationTime" : "Fri Jan 01 00:00:00 UTC 1904",
+    "Balance" : "0",
+    "Format" : "MPEG-4, Advanced Audio Coding (AAC)",
+    "NumberOfChannels" : "1",
+    "SampleSize" : "16",
+    "SampleRate" : "44100"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/mp4.json b/exec/java-exec/src/test/resources/store/image/mp4.json
new file mode 100644
index 0000000..1e581c3
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/mp4.json
@@ -0,0 +1,56 @@
+{
+  "Format" : "MP4",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "72",
+  "DPIHeight" : "72",
+  "PixelWidth" : "560",
+  "PixelHeight" : "320",
+  "BitsPerPixel" : "24",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:05",
+  "VideoCodec" : "JVT/AVC Coding",
+  "FrameRate" : "30",
+  "AudioCodec" : "Sat Mar 20 21:29:11 UTC 2010",
+  "AudioSampleSize" : "16",
+  "AudioSampleRate" : "48000",
+  "FileType" : {
+    "DetectedFileTypeName" : "MP4",
+    "DetectedFileTypeLongName" : "MPEG-4 Part 14",
+    "DetectedMIMEType" : "video/mp4",
+    "ExpectedFileNameExtension" : "mp4"
+  },
+  "MP4" : {
+    "MajorBrand" : "MP4 v2 [ISO 14496-14]",
+    "MinorVersion" : "0",
+    "CompatibleBrands" : "[MP4 v2 [ISO 14496-14], MP4  Base Media v1 [IS0 14496-12:2003], MP4 Base w/ AVC ext [ISO 14496-12:2005]]",
+    "CreationTime" : "Sat Mar 20 21:29:11 UTC 2010",
+    "ModificationTime" : "Sat Mar 20 21:29:12 UTC 2010",
+    "Duration" : "00:00:05",
+    "MediaTimeScale" : "90000",
+    "TransformationMatrix" : "65536 0 0 0 65536 0 0 0 1073741824",
+    "PreferredRate" : "1",
+    "PreferredVolume" : "1",
+    "NextTrackID" : "3"
+  },
+  "MP4Video" : {
+    "Vendor" : "Sat Mar 20 21:29:11 UTC 2010",
+    "TemporalQuality" : "Sat Mar 20 21:29:12 UTC 2010",
+    "Width" : "560 pixels",
+    "Opcolor" : "0 0 0",
+    "GraphicsMode" : "Copy",
+    "Height" : "320 pixels",
+    "CompressionType" : "JVT/AVC Coding",
+    "Depth" : "Unknown (24)",
+    "HorizontalResolution" : "72",
+    "VerticalResolution" : "72",
+    "FrameRate" : "30"
+  },
+  "MP4Sound" : {
+    "Format" : "Sat Mar 20 21:29:11 UTC 2010",
+    "NumberOfChannels" : "1",
+    "SampleRate" : "48000",
+    "Balance" : "0",
+    "SampleSize" : "16"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/pcx.json b/exec/java-exec/src/test/resources/store/image/pcx.json
new file mode 100644
index 0000000..92d4816
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/pcx.json
@@ -0,0 +1,37 @@
+{
+  "Format" : "PCX",
+  "PixelWidth" : "128",
+  "PixelHeight" : "174",
+  "DPIWidth" : "72",
+  "DPIHeight" : "72",
+  "BitsPerPixel" : "24",
+  "HasAlpha" : "false",
+  "Orientaion" : "Unknown (0)",
+  "ColorMode" : "RGB",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "PCX",
+    "DetectedFileTypeLongName" : "PiCture eXchange",
+    "DetectedMIMEType" : "image/x-pcx",
+    "ExpectedFileNameExtension" : "pcx"
+  },
+  "PCX" : {
+    "Version" : "3.0 or better",
+    "BitsPerPixel" : "8",
+    "XMin" : "0",
+    "YMin" : "0",
+    "XMax" : "127",
+    "YMax" : "173",
+    "HorizontalDPI" : "72",
+    "VerticalDPI" : "72",
+    "Palette" : "[48 values]",
+    "ColorPlanes" : "24-bit color",
+    "BytesPerLine" : "128",
+    "PaletteType" : "Color or B&W"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/png.json b/exec/java-exec/src/test/resources/store/image/png.json
new file mode 100644
index 0000000..a883d5b
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/png.json
@@ -0,0 +1,57 @@
+{
+  "Format" : "PNG",
+  "PixelWidth" : "128",
+  "PixelHeight" : "174",
+  "HasAlpha" : "true",
+  "BitsPerPixel" : "32",
+  "DPIWidth" : "72.009",
+  "DPIHeight" : "72.009",
+  "Orientaion" : "Unknown (0)",
+  "ColorMode" : "RGB",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "PNG",
+    "DetectedFileTypeLongName" : "Portable Network Graphics",
+    "DetectedMIMEType" : "image/png",
+    "ExpectedFileNameExtension" : "png"
+  },
+  "PNGIHDR" : {
+    "ImageWidth" : "128",
+    "ImageHeight" : "174",
+    "BitsPerSample" : "8",
+    "ColorType" : "True Color with Alpha",
+    "CompressionType" : "Deflate",
+    "FilterMethod" : "Adaptive",
+    "InterlaceMethod" : "No Interlace"
+  },
+  "PNGTEXt" : {
+    "TextualData" : [
+      "date:create: 2015-06-22T09:06:26-04:00",
+      "date:modify: 2015-06-22T09:06:26-04:00"
+    ]
+  },
+  "PNGSRGB" : {
+    "SRGBRenderingIntent" : "Perceptual"
+  },
+  "PNGPHYs" : {
+    "PixelsPerUnitX" : "2835",
+    "PixelsPerUnitY" : "2835",
+    "UnitSpecifier" : "Metres"
+  },
+  "PNGChromaticities" : {
+    "WhitePointX" : "31269",
+    "WhitePointY" : "32899",
+    "RedX" : "63999",
+    "RedY" : "33001",
+    "GreenX" : "30000",
+    "GreenY" : "60000",
+    "BlueX" : "15000",
+    "BlueY" : "5999"
+  },
+  "PNGBKGD" : { }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/psd.json b/exec/java-exec/src/test/resources/store/image/psd.json
new file mode 100644
index 0000000..a8d646a
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/psd.json
@@ -0,0 +1,119 @@
+{
+  "Format" : "PSD",
+  "Orientaion" : "Top, left side (Horizontal / normal)",
+  "DPIWidth" : "72",
+  "DPIHeight" : "72",
+  "PixelWidth" : "128",
+  "PixelHeight" : "174",
+  "BitsPerPixel" : "32",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "PSD",
+    "DetectedFileTypeLongName" : "Photoshop Document",
+    "DetectedMIMEType" : "image/vnd.adobe.photoshop",
+    "ExpectedFileNameExtension" : "psd"
+  },
+  "ExifIFD0" : {
+    "Orientation" : "Top, left side (Horizontal / normal)",
+    "XResolution" : "72009/1000 dots per inch",
+    "YResolution" : "72009/1000 dots per inch",
+    "ResolutionUnit" : "Inch",
+    "Software" : "Adobe Photoshop CS2 Windows",
+    "DateTime" : "2016:02:06 00:08:57"
+  },
+  "ExifSubIFD" : {
+    "ColorSpace" : "Undefined",
+    "ExifImageWidth" : "128 pixels",
+    "ExifImageHeight" : "174 pixels"
+  },
+  "ExifThumbnail" : {
+    "Compression" : "JPEG (old-style)",
+    "XResolution" : "72 dots per inch",
+    "YResolution" : "72 dots per inch",
+    "ResolutionUnit" : "Inch",
+    "ThumbnailOffset" : "302 bytes",
+    "ThumbnailLength" : "0 bytes"
+  },
+  "XMP" : {
+    "XMPValueCount" : "22",
+    "Xmp" : {
+      "ModifyDate" : "2016-02-06T00:08:57+09:00",
+      "MetadataDate" : "2016-02-06T00:08:57+09:00",
+      "CreatorTool" : "Adobe Photoshop CS2 Windows",
+      "CreateDate" : "2016-02-06T00:08:57+09:00"
+    },
+    "Photoshop" : {
+      "History" : "",
+      "ICCProfile" : "sRGB IEC61966-2.1",
+      "ColorMode" : "3"
+    },
+    "Tiff" : {
+      "XResolution" : "720090/10000",
+      "NativeDigest" : "256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;F3A2BBED3F60568C7329EB637603055D",
+      "ResolutionUnit" : "2",
+      "Orientation" : "1",
+      "YResolution" : "720090/10000"
+    },
+    "XmpMM" : {
+      "DerivedFrom": {
+        "DocumentID" : "uuid:365756FF19CCE511BCD0B4FE57F853AF",
+        "InstanceID" : "uuid:365756FF19CCE511BCD0B4FE57F853AF"
+      },
+      "DocumentID" : "uuid:375756FF19CCE511BCD0B4FE57F853AF",
+      "InstanceID" : "uuid:385756FF19CCE511BCD0B4FE57F853AF"
+    },
+    "Exif" : {
+      "PixelYDimension" : "174",
+      "ColorSpace" : "-1",
+      "PixelXDimension" : "128",
+      "NativeDigest" : "36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;07837EFC5AF54CDBA4998B26FEFAF26C"
+    },
+    "Dc" : {
+      "Format" : "application/vnd.adobe.photoshop"
+    }
+  },
+  "Photoshop" : {
+    "CaptionDigest" : "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
+    "ResolutionInfo" : "72.01x72.01 DPI",
+    "PrintScale" : "Centered, Scale 1.0",
+    "AlphaChannels" : "8 147 167 150 190 149 148 149 170",
+    "UnicodeAlphaNames" : "0 0 0 5 144 15 102 14 144 232 82 6 0 0",
+    "DisplayInfo(Obsolete)" : "0 0 255 255 0 0 0 0 0 0 0 100 1 0",
+    "AlphaIdentifiers" : "0 0 0 0",
+    "GlobalAngle" : "30",
+    "GlobalAltitude" : "30",
+    "PrintFlags" : "0 0 0 0 0 0 0 0 1",
+    "CopyrightFlag" : "No",
+    "PrintFlagsInformation" : "0 1 0 0 0 0 0 0 0 2",
+    "ColorHalftoningInformation" : "[72 values]",
+    "ColorTransferFunctions" : "[112 values]",
+    "LayerStateInformation" : "0 0",
+    "LayersGroupInformation" : "0 0",
+    "LayerGroupsEnabledID" : "1",
+    "LayerSelectionIDs" : "0 1 0 0 0 3",
+    "GridAndGuidesInformation" : "0 0 0 1 0 0 2 64 0 0 2 64 0 0 0 0",
+    "URLList" : "0",
+    "Slices" : "rose-128x174-alpha (0,0,174,128) 1 Slices",
+    "PixelAspectRatio" : "1.0",
+    "ICCUntaggedProfile" : "1",
+    "SeedNumber" : "3",
+    "ThumbnailData" : "JpegRGB, 118x160, Decomp 56960 bytes, 1572865 bpp, 3786 bytes",
+    "VersionInfo" : "1 (Adobe Photoshop, Adobe Photoshop CS2) 1",
+    "PlugIn1Data" : "[268 values]",
+    "PlugIn2Data" : "[28 values]"
+  },
+  "PSDHeader" : {
+    "ChannelCount" : "4 channels",
+    "ImageHeight" : "174 pixels",
+    "ImageWidth" : "128 pixels",
+    "BitsPerChannel" : "8 bits per channel",
+    "ColorMode" : "RGB"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit-lzw.tiff b/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit-lzw.tiff
new file mode 100644
index 0000000..79530d3
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit-lzw.tiff differ
diff --git a/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit.bmp b/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit.bmp
new file mode 100644
index 0000000..50fec90
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit.bmp differ
diff --git a/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit.pcx b/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit.pcx
new file mode 100644
index 0000000..3ace47d
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-128x174-24bit.pcx differ
diff --git a/exec/java-exec/src/test/resources/store/image/rose-128x174-32bit-alpha.png b/exec/java-exec/src/test/resources/store/image/rose-128x174-32bit-alpha.png
new file mode 100644
index 0000000..8606080
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-128x174-32bit-alpha.png differ
diff --git a/exec/java-exec/src/test/resources/store/image/rose-128x174-32bit-alpha.psd b/exec/java-exec/src/test/resources/store/image/rose-128x174-32bit-alpha.psd
new file mode 100644
index 0000000..77643e2
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-128x174-32bit-alpha.psd differ
diff --git a/exec/java-exec/src/test/resources/store/image/rose-128x174-8bit-alpha.gif b/exec/java-exec/src/test/resources/store/image/rose-128x174-8bit-alpha.gif
new file mode 100644
index 0000000..61550b2
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-128x174-8bit-alpha.gif differ
diff --git a/exec/java-exec/src/test/resources/store/image/rose-32x32-32bit-alpha.ico b/exec/java-exec/src/test/resources/store/image/rose-32x32-32bit-alpha.ico
new file mode 100644
index 0000000..027a276
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/rose-32x32-32bit-alpha.ico differ
diff --git a/exec/java-exec/src/test/resources/store/image/sample.avi b/exec/java-exec/src/test/resources/store/image/sample.avi
new file mode 100644
index 0000000..850feab
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/sample.avi differ
diff --git a/exec/java-exec/src/test/resources/store/image/sample.mov b/exec/java-exec/src/test/resources/store/image/sample.mov
new file mode 100644
index 0000000..5e9d178
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/sample.mov differ
diff --git a/exec/java-exec/src/test/resources/store/image/sample.mp4 b/exec/java-exec/src/test/resources/store/image/sample.mp4
new file mode 100644
index 0000000..1fc4788
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/sample.mp4 differ
diff --git a/exec/java-exec/src/test/resources/store/image/sample.wav b/exec/java-exec/src/test/resources/store/image/sample.wav
new file mode 100644
index 0000000..d71512a
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/sample.wav differ
diff --git a/exec/java-exec/src/test/resources/store/image/tiff.json b/exec/java-exec/src/test/resources/store/image/tiff.json
new file mode 100644
index 0000000..9c26a72
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/tiff.json
@@ -0,0 +1,87 @@
+{
+  "Format" : "ARW",
+  "PixelWidth" : "128",
+  "PixelHeight" : "174",
+  "Orientaion" : "Top, left side (Horizontal / normal)",
+  "DPIWidth" : "72",
+  "DPIHeight" : "72",
+  "BitsPerPixel" : "24",
+  "ColorMode" : "RGB",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "ARW",
+    "DetectedFileTypeLongName" : "Sony Camera Raw",
+    "ExpectedFileNameExtension" : "arw"
+  },
+  "ExifIFD0" : {
+    "NewSubfileType" : "Full-resolution image",
+    "ImageWidth" : "128 pixels",
+    "ImageHeight" : "174 pixels",
+    "BitsPerSample" : "8 8 8 bits/component/pixel",
+    "Compression" : "LZW",
+    "PhotometricInterpretation" : "RGB",
+    "StripOffsets" : "23876",
+    "Orientation" : "Top, left side (Horizontal / normal)",
+    "SamplesPerPixel" : "3 samples/pixel",
+    "RowsPerStrip" : "174 rows/strip",
+    "StripByteCounts" : "26556 bytes",
+    "XResolution" : "72009/1000 dots per inch",
+    "YResolution" : "72009/1000 dots per inch",
+    "PlanarConfiguration" : "Chunky (contiguous for each subsampling pixel)",
+    "ResolutionUnit" : "Inch",
+    "Software" : "Adobe Photoshop CS2 Windows",
+    "DateTime" : "2016:02:05 01:25:42",
+    "Predictor" : "2",
+    "UnknownTag(0x8649)" : "[5390 values]",
+    "InterColorProfile" : "[3144 values]"
+  },
+  "ExifSubIFD" : {
+    "ColorSpace" : "sRGB",
+    "ExifImageWidth" : "128 pixels",
+    "ExifImageHeight" : "174 pixels"
+  },
+  "XMP" : {
+    "XMPValueCount" : "22",
+    "Xmp" : {
+      "ModifyDate" : "2016-02-05T01:25:42+09:00",
+      "MetadataDate" : "2016-02-05T01:25:42+09:00",
+      "CreatorTool" : "Adobe Photoshop CS2 Windows",
+      "CreateDate" : "2016-02-05T01:25:42+09:00"
+    },
+    "Photoshop" : {
+      "History" : "",
+      "ICCProfile" : "sRGB IEC61966-2.1",
+      "ColorMode" : "3"
+    },
+    "Tiff" : {
+      "XResolution" : "720090/10000",
+      "NativeDigest" : "256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;6A3819C79FDE56A3CEB49BE0CECF0E4B",
+      "ResolutionUnit" : "2",
+      "Orientation" : "1",
+      "YResolution" : "720090/10000"
+    },
+    "XmpMM" : {
+      "DerivedFrom" : {
+        "DocumentID" : "uuid:755EFE1B5BCBE51191D2BA1A4A34CC1F",
+        "InstanceID" : "uuid:765EFE1B5BCBE51191D2BA1A4A34CC1F"
+      },
+      "DocumentID" : "uuid:785EFE1B5BCBE51191D2BA1A4A34CC1F",
+      "InstanceID" : "uuid:795EFE1B5BCBE51191D2BA1A4A34CC1F"
+    },
+    "Exif" : {
+      "PixelYDimension" : "174",
+      "ColorSpace" : "1",
+      "PixelXDimension" : "128",
+      "NativeDigest" : "36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;EB685DEADF67388F7E939885A41C0ECF"
+    },
+    "Dc" : {
+      "Format" : "image/tiff"
+    }
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/wav.json b/exec/java-exec/src/test/resources/store/image/wav.json
new file mode 100644
index 0000000..0823442
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/wav.json
@@ -0,0 +1,32 @@
+{
+  "Format" : "WAV",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "0",
+  "DPIHeight" : "0",
+  "PixelWidth" : "0",
+  "PixelHeight" : "0",
+  "BitsPerPixel" : "0",
+  "ColorMode" : "N/A",
+  "HasAlpha" : "false",
+  "Duration" : "00:00:03",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Microsoft PCM",
+  "AudioSampleSize" : "8",
+  "AudioSampleRate" : "11025",
+  "FileType" : {
+    "DetectedFileTypeName" : "WAV",
+    "DetectedFileTypeLongName" : "Waveform Audio File Format",
+    "DetectedMIMEType" : "audio/vnd.wave",
+    "ExpectedFileNameExtension" : "wav"
+  },
+  "WAV" : {
+    "BitsPerSample" : "8",
+    "Format" : "Microsoft PCM",
+    "Channels" : "1",
+    "SamplesPerSecond" : "11025",
+    "BytesPerSecond" : "11025",
+    "BlockAlignment" : "1",
+    "Duration" : "00:00:03"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/webp.json b/exec/java-exec/src/test/resources/store/image/webp.json
new file mode 100644
index 0000000..e602506
--- /dev/null
+++ b/exec/java-exec/src/test/resources/store/image/webp.json
@@ -0,0 +1,29 @@
+{
+  "Format" : "WEBP",
+  "PixelWidth" : "400",
+  "PixelHeight" : "301",
+  "HasAlpha" : "true",
+  "Orientaion" : "Unknown (0)",
+  "DPIWidth" : "0",
+  "DPIHeight" : "0",
+  "ColorMode" : "RGB",
+  "BitsPerPixel" : "0",
+  "Duration" : "00:00:00",
+  "VideoCodec" : "Unknown",
+  "FrameRate" : "0",
+  "AudioCodec" : "Unknown",
+  "AudioSampleSize" : "0",
+  "AudioSampleRate" : "0",
+  "FileType" : {
+    "DetectedFileTypeName" : "WebP",
+    "DetectedFileTypeLongName" : "WebP",
+    "DetectedMIMEType" : "image/webp",
+    "ExpectedFileNameExtension" : "webp"
+  },
+  "WebP" : {
+    "ImageWidth" : "400",
+    "ImageHeight" : "301",
+    "HasAlpha" : "true",
+    "IsAnimation" : "false"
+  }
+}
diff --git a/exec/java-exec/src/test/resources/store/image/withExifAndIptc.jpg b/exec/java-exec/src/test/resources/store/image/withExifAndIptc.jpg
new file mode 100644
index 0000000..c9e425d
Binary files /dev/null and b/exec/java-exec/src/test/resources/store/image/withExifAndIptc.jpg differ
diff --git a/exec/jdbc-all/pom.xml b/exec/jdbc-all/pom.xml
index 4345c39..d8423de 100644
--- a/exec/jdbc-all/pom.xml
+++ b/exec/jdbc-all/pom.xml
@@ -172,6 +172,10 @@
           <groupId>commons-validator</groupId>
           <artifactId>commons-validator</artifactId>
         </exclusion>
+        <exclusion>
+          <artifactId>metadata-extractor</artifactId>
+          <groupId>com.drewnoakes</groupId>
+        </exclusion>
       </exclusions>
     </dependency>
     <dependency>

-- 
To stop receiving notification emails like this one, please contact
parthc@apache.org.