
org.modeshape.sequencer.image.ImageMetadataSequencer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of modeshape-sequencer-images Show documentation
Show all versions of modeshape-sequencer-images Show documentation
ModeShape Sequencer that processes PNG, JPEG, GIF and other image files
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.sequencer.image;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.jcr.Binary;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.StringUtil;
import org.modeshape.jcr.api.JcrConstants;
import org.modeshape.jcr.api.nodetype.NodeTypeManager;
import org.modeshape.jcr.api.sequencer.Sequencer;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Descriptor;
import com.drew.metadata.exif.ExifIFD0Directory;
/**
* A sequencer that processes the binary content of an image file, extracts the metadata for the image, and then writes that image
* metadata to the repository.
*
* This sequencer produces data that corresponds to the following structure:
*
* - image:metadata node of type
image:metadata
*
* - jcr:mimeType - optional string property for the mime type of the image
* - jcr:encoding - optional string property for the encoding of the image
* - image:formatName - string property for the name of the format
* - image:width - optional integer property for the image's width in pixels
* - image:height - optional integer property for the image's height in pixels
* - image:bitsPerPixel - optional integer property for the number of bits per pixel
* - image:progressive - optional boolean property specifying whether the image is stored in a progressive
* (i.e., interlaced) form
* - image:numberOfImages - optional integer property for the number of images stored in the file; defaults to
* 1
* - image:physicalWidthDpi - optional integer property for the physical width of the image in dots per inch
* - image:physicalHeightDpi - optional integer property for the physical height of the image in dots per inch
* - image:physicalWidthInches - optional double property for the physical width of the image in inches
* - image:physicalHeightInches - optional double property for the physical height of the image in inches
* - image:exif - optional child node which contains additional EXIF information (if available)
*
* - image:artist
* - image:copyright
* - image:datetime
* - image:description
* - image:make
* - image:model
* - image:unit
* - image:orientation
* - image:software
* - image:resolution_x
* - image:resolution_y
*
*
*
*
*
*/
public class ImageMetadataSequencer extends Sequencer {
private static final int[] EXIF_TAGS = new int[] { ExifIFD0Directory.TAG_ARTIST, ExifIFD0Directory.TAG_COPYRIGHT, ExifIFD0Directory.TAG_DATETIME,
ExifIFD0Directory.TAG_IMAGE_DESCRIPTION, ExifIFD0Directory.TAG_MAKE, ExifIFD0Directory.TAG_MODEL, ExifIFD0Directory.TAG_RESOLUTION_UNIT,
ExifIFD0Directory.TAG_SOFTWARE, ExifIFD0Directory.TAG_X_RESOLUTION, ExifIFD0Directory.TAG_Y_RESOLUTION, ExifIFD0Directory.TAG_ORIENTATION
};
private static final String TIFF_FORMAT = "TIFF";
private static final String[] TIFF_MIME_TYPES = new String[] {"image/tiff", "image/x-tiff", "image/tif", "image/x-tif", "application/tif", "application/x-tif",
"application/tiff", "application/x-tiff" };
private static final String IMAGE_TIFF_DEFAULT_MIME_TYPE = TIFF_MIME_TYPES[0];
@Override
public boolean execute( Property inputProperty,
Node outputNode,
Context context ) throws Exception {
Binary binaryValue = inputProperty.getBinary();
CheckArg.isNotNull(binaryValue, "binary");
Node imageNode = getImageMetadataNode(outputNode);
boolean imageParsedUsingDefaultMetadata = processUsingDefaultMetadata(imageNode, binaryValue);
return processUsingAdvancedMetadata(imageNode, binaryValue, imageParsedUsingDefaultMetadata);
}
private boolean processUsingAdvancedMetadata( Node imageNode,
Binary binaryValue,
boolean imageParsedUsingDefaultMetadata ) throws Exception {
try (InputStream stream = binaryValue.getStream()) {
Metadata advancedMetadata = ImageMetadataReader.readMetadata(new BufferedInputStream(stream), false);
ExifIFD0Directory exifIFD0Directory = advancedMetadata.getDirectory(ExifIFD0Directory.class);
if (exifIFD0Directory == null || !hasTags(exifIFD0Directory, EXIF_TAGS)) {
if (!imageParsedUsingDefaultMetadata) {
getLogger().info("Neither default nor advanced metadata parser can resolve image. Ignoring sequencing");
}
getLogger().debug("No relevant IFD0 information found, ignoring EXIF node.");
return imageParsedUsingDefaultMetadata;
}
if (!imageParsedUsingDefaultMetadata) {
//it's a format not supported by the default metadata and since we're reading the IFD0 descriptor, mark the image
//as a TIFF
getLogger().info(
"Image has IFD0 block information but is not one of the standard image types, marking it as TIFF");
imageNode.setProperty(ImageMetadataLexicon.FORMAT_NAME, TIFF_FORMAT);
imageNode.setProperty(JcrConstants.JCR_MIME_TYPE, IMAGE_TIFF_DEFAULT_MIME_TYPE);
}
addEXIFNode(imageNode, exifIFD0Directory);
return true;
} catch (Exception e) {
getLogger().debug(e, "Cannot process image for advanced metadata");
return imageParsedUsingDefaultMetadata;
}
}
private void addEXIFNode( Node imageNode,
ExifIFD0Directory exifIFD0Directory ) throws RepositoryException {
ExifIFD0Descriptor exifDescriptor = new ExifIFD0Descriptor(exifIFD0Directory);
Node exifNode = imageNode.addNode(ImageMetadataLexicon.EXIF_NODE, ImageMetadataLexicon.EXIF_NODE);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.ARTIST, exifIFD0Directory, ExifIFD0Directory.TAG_ARTIST);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.COPYRIGHT, exifIFD0Directory, ExifIFD0Directory.TAG_COPYRIGHT);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.DESCRIPTION, exifIFD0Directory, ExifIFD0Directory.TAG_IMAGE_DESCRIPTION);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.MAKE, exifIFD0Directory, ExifIFD0Directory.TAG_MAKE);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.MODEL, exifIFD0Directory, ExifIFD0Directory.TAG_MODEL);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.SOFTWARE, exifIFD0Directory, ExifIFD0Directory.TAG_SOFTWARE);
setStringIfTagPresent(exifNode, ImageMetadataLexicon.DATETIME, exifIFD0Directory, ExifIFD0Directory.TAG_DATETIME);
setStringIfNotBlank(exifNode, ImageMetadataLexicon.UNIT, exifDescriptor.getResolutionDescription());
setStringIfNotBlank(exifNode, ImageMetadataLexicon.ORIENTATION, exifDescriptor.getOrientationDescription());
setDoubleIfTagPresent(exifNode, ImageMetadataLexicon.RESOLUTION_X, exifIFD0Directory,
ExifIFD0Directory.TAG_X_RESOLUTION);
setDoubleIfTagPresent(exifNode, ImageMetadataLexicon.RESOLUTION_Y, exifIFD0Directory,
ExifIFD0Directory.TAG_Y_RESOLUTION);
}
private void setStringIfTagPresent(Node node, String propertyName, Directory directory, int tag) throws RepositoryException {
setStringIfNotBlank(node, propertyName, directory.getString(tag));
}
private void setStringIfNotBlank( Node node,
String propertyName,
String value ) throws RepositoryException {
if (!StringUtil.isBlank(value)) {
node.setProperty(propertyName, value);
}
}
private void setDoubleIfTagPresent( Node node,
String propertyName,
Directory directory,
int tag ) throws RepositoryException {
if (!directory.containsTag(tag)) {
return;
}
node.setProperty(propertyName, directory.getRational(tag).doubleValue());
}
private boolean hasTags(Directory directory, int... tags) {
for (int tag : tags) {
if (directory.containsTag(tag)) {
return true;
}
}
return false;
}
private boolean processUsingDefaultMetadata( Node imageNode,
Binary binaryValue ) throws Exception {
InputStream stream = binaryValue.getStream();
try {
ImageMetadata metadata = new ImageMetadata();
metadata.setInput(stream);
metadata.setDetermineImageNumber(true);
metadata.setCollectComments(true);
// Process the image stream and extract the metadata ...
if (!metadata.check()) {
getLogger().debug("Unknown format detected using default metadata parser.");
return false;
}
setImagePropertiesOnNode(imageNode, metadata);
return true;
} finally {
stream.close();
}
}
private Node getImageMetadataNode( Node outputNode ) throws RepositoryException {
if (outputNode.isNew()) {
outputNode.setPrimaryType(ImageMetadataLexicon.METADATA_NODE);
return outputNode;
}
return outputNode.addNode(ImageMetadataLexicon.METADATA_NODE, ImageMetadataLexicon.METADATA_NODE);
}
private void setImagePropertiesOnNode( Node node,
ImageMetadata metadata ) throws Exception {
node.setProperty(JcrConstants.JCR_MIME_TYPE, metadata.getMimeType());
node.setProperty(ImageMetadataLexicon.FORMAT_NAME, metadata.getFormatName());
node.setProperty(ImageMetadataLexicon.WIDTH, metadata.getWidth());
node.setProperty(ImageMetadataLexicon.HEIGHT, metadata.getHeight());
node.setProperty(ImageMetadataLexicon.BITS_PER_PIXEL, metadata.getBitsPerPixel());
node.setProperty(ImageMetadataLexicon.PROGRESSIVE, metadata.isProgressive());
node.setProperty(ImageMetadataLexicon.NUMBER_OF_IMAGES, metadata.getNumberOfImages());
node.setProperty(ImageMetadataLexicon.PHYSICAL_WIDTH_DPI, metadata.getPhysicalWidthDpi());
node.setProperty(ImageMetadataLexicon.PHYSICAL_HEIGHT_DPI, metadata.getPhysicalHeightDpi());
node.setProperty(ImageMetadataLexicon.PHYSICAL_WIDTH_INCHES, metadata.getPhysicalWidthInch());
node.setProperty(ImageMetadataLexicon.PHYSICAL_HEIGHT_INCHES, metadata.getPhysicalHeightInch());
}
@Override
public void initialize( NamespaceRegistry registry,
NodeTypeManager nodeTypeManager ) throws RepositoryException, IOException {
registerNodeTypes("images.cnd", nodeTypeManager, true);
registerDefaultMimeTypes(ImageMetadata.MIME_TYPE_STRINGS);
registerDefaultMimeTypes(TIFF_MIME_TYPES);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy