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 <[email protected]> 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 [email protected].
