001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.sequencer.image; 025 026 import java.io.InputStream; 027 import org.jboss.dna.graph.property.NameFactory; 028 import org.jboss.dna.graph.property.Path; 029 import org.jboss.dna.graph.property.PathFactory; 030 import org.jboss.dna.graph.sequencer.StreamSequencerContext; 031 import org.jboss.dna.graph.sequencer.SequencerOutput; 032 import org.jboss.dna.graph.sequencer.StreamSequencer; 033 034 /** 035 * A sequencer that processes the binary content of an image file, extracts the metadata for the image, and then writes that image 036 * metadata to the repository. 037 * <p> 038 * This sequencer produces data that corresponds to the following structure: 039 * <ul> 040 * <li><strong>image:metadata</strong> node of type <code>image:metadata</code> 041 * <ul> 042 * <li><strong>jcr:mimeType</strong> - optional string property for the mime type of the image</li> 043 * <li><strong>jcr:encoding</strong> - optional string property for the encoding of the image</li> 044 * <li><strong>image:formatName</strong> - string property for the name of the format</li> 045 * <li><strong>image:width</strong> - optional integer property for the image's width in pixels</li> 046 * <li><strong>image:height</strong> - optional integer property for the image's height in pixles</li> 047 * <li><strong>image:bitsPerPixel</strong> - optional integer property for the number of bits per pixel</li> 048 * <li><strong>image:progressive</strong> - optional boolean property specifying whether the image is stored in a progressive 049 * (i.e., interlaced) form</li> 050 * <li><strong>image:numberOfImages</strong> - optional integer property for the number of images stored in the file; defaults to 051 * 1</li> 052 * <li><strong>image:physicalWidthDpi</strong> - optional integer property for the physical width of the image in dots per inch</li> 053 * <li><strong>image:physicalHeightDpi</strong> - optional integer property for the physical height of the image in dots per inch</li> 054 * <li><strong>image:physicalWidthInches</strong> - optional double property for the physical width of the image in inches</li> 055 * <li><strong>image:physicalHeightInches</strong> - optional double property for the physical height of the image in inches</li> 056 * </ul> 057 * </li> 058 * </ul> 059 * </p> 060 * <p> 061 * This structure could be extended in the future to add EXIF and IPTC metadata as child nodes. For example, EXIF metadata is 062 * structured as tags in directories, where the directories form something like namespaces, and which are used by different camera 063 * vendors to store custom metadata. This structure could be mapped with each directory (e.g. "EXIF" or "Nikon Makernote" or 064 * "IPTC") as the name of a child node, with the EXIF tags values stored as either properties or child nodes. 065 * </p> 066 * 067 * @author Randall Hauch 068 * @author John Verhaeg 069 */ 070 public class ImageMetadataSequencer implements StreamSequencer { 071 072 public static final String METADATA_NODE = "image:metadata"; 073 public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType"; 074 public static final String IMAGE_MIXINS = "jcr:mixinTypes"; 075 public static final String IMAGE_MIME_TYPE = "jcr:mimeType"; 076 public static final String IMAGE_ENCODING = "jcr:encoding"; 077 public static final String IMAGE_FORMAT_NAME = "image:formatName"; 078 public static final String IMAGE_WIDTH = "image:width"; 079 public static final String IMAGE_HEIGHT = "image:height"; 080 public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel"; 081 public static final String IMAGE_PROGRESSIVE = "image:progressive"; 082 public static final String IMAGE_NUMBER_OF_IMAGES = "image:numberOfImages"; 083 public static final String IMAGE_PHYSICAL_WIDTH_DPI = "image:physicalWidthDpi"; 084 public static final String IMAGE_PHYSICAL_HEIGHT_DPI = "image:physicalHeightDpi"; 085 public static final String IMAGE_PHYSICAL_WIDTH_INCHES = "image:physicalWidthInches"; 086 public static final String IMAGE_PHYSICAL_HEIGHT_INCHES = "image:physicalHeightInches"; 087 088 /** 089 * {@inheritDoc} 090 * 091 * @see StreamSequencer#sequence(InputStream, SequencerOutput, StreamSequencerContext) 092 */ 093 public void sequence( InputStream stream, 094 SequencerOutput output, 095 StreamSequencerContext context ) { 096 097 ImageMetadata metadata = new ImageMetadata(); 098 metadata.setInput(stream); 099 metadata.setDetermineImageNumber(true); 100 metadata.setCollectComments(true); 101 102 // Process the image stream and extract the metadata ... 103 if (!metadata.check()) { 104 metadata = null; 105 } 106 107 // Generate the output graph if we found useful metadata ... 108 if (metadata != null) { 109 NameFactory nameFactory = context.getValueFactories().getNameFactory(); 110 PathFactory pathFactory = context.getValueFactories().getPathFactory(); 111 Path metadataNode = pathFactory.create(METADATA_NODE); 112 113 // Place the image metadata into the output map ... 114 output.setProperty(metadataNode, nameFactory.create(IMAGE_PRIMARY_TYPE), "image:metadata"); 115 // output.psetProperty(metadataNode, nameFactory.create(IMAGE_MIXINS), ""); 116 output.setProperty(metadataNode, nameFactory.create(IMAGE_MIME_TYPE), metadata.getMimeType()); 117 // output.setProperty(metadataNode, nameFactory.create(IMAGE_ENCODING), ""); 118 output.setProperty(metadataNode, nameFactory.create(IMAGE_FORMAT_NAME), metadata.getFormatName()); 119 output.setProperty(metadataNode, nameFactory.create(IMAGE_WIDTH), metadata.getWidth()); 120 output.setProperty(metadataNode, nameFactory.create(IMAGE_HEIGHT), metadata.getHeight()); 121 output.setProperty(metadataNode, nameFactory.create(IMAGE_BITS_PER_PIXEL), metadata.getBitsPerPixel()); 122 output.setProperty(metadataNode, nameFactory.create(IMAGE_PROGRESSIVE), metadata.isProgressive()); 123 output.setProperty(metadataNode, nameFactory.create(IMAGE_NUMBER_OF_IMAGES), metadata.getNumberOfImages()); 124 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_WIDTH_DPI), metadata.getPhysicalWidthDpi()); 125 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_HEIGHT_DPI), metadata.getPhysicalHeightDpi()); 126 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_WIDTH_INCHES), metadata.getPhysicalWidthInch()); 127 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_HEIGHT_INCHES), metadata.getPhysicalHeightInch()); 128 } 129 } 130 }