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.