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