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.common.monitor.ProgressMonitor; 026 import org.jboss.dna.graph.properties.NameFactory; 027 import org.jboss.dna.graph.properties.Path; 028 import org.jboss.dna.graph.properties.PathFactory; 029 import org.jboss.dna.graph.sequencers.SequencerContext; 030 import org.jboss.dna.graph.sequencers.SequencerOutput; 031 import org.jboss.dna.graph.sequencers.StreamSequencer; 032 033 /** 034 * A sequencer that processes the binary content of an image file, extracts the metadata for the image, and then writes that image 035 * metadata to the repository. 036 * <p> 037 * This sequencer produces data that corresponds to the following structure: 038 * <ul> 039 * <li><strong>image:metadata</strong> node of type <code>image:metadata</code> 040 * <ul> 041 * <li><strong>jcr:mimeType</strong> - optional string property for the mime type of the image</li> 042 * <li><strong>jcr:encoding</strong> - optional string property for the encoding of the image</li> 043 * <li><strong>image:formatName</strong> - string property for the name of the format</li> 044 * <li><strong>image:width</strong> - optional integer property for the image's width in pixels</li> 045 * <li><strong>image:height</strong> - optional integer property for the image's height in pixles</li> 046 * <li><strong>image:bitsPerPixel</strong> - optional integer property for the number of bits per pixel</li> 047 * <li><strong>image:progressive</strong> - optional boolean property specifying whether the image is stored in a progressive 048 * (i.e., interlaced) form</li> 049 * <li><strong>image:numberOfImages</strong> - optional integer property for the number of images stored in the file; defaults 050 * to 1</li> 051 * <li><strong>image:physicalWidthDpi</strong> - optional integer property for the physical width of the image in dots per inch</li> 052 * <li><strong>image:physicalHeightDpi</strong> - optional integer property for the physical height of the image in dots per 053 * 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 */ 069 public class ImageMetadataSequencer implements StreamSequencer { 070 071 public static final String METADATA_NODE = "image:metadata"; 072 public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType"; 073 public static final String IMAGE_MIXINS = "jcr:mixinTypes"; 074 public static final String IMAGE_MIME_TYPE = "jcr:mimeType"; 075 public static final String IMAGE_ENCODING = "jcr:encoding"; 076 public static final String IMAGE_FORMAT_NAME = "image:formatName"; 077 public static final String IMAGE_WIDTH = "image:width"; 078 public static final String IMAGE_HEIGHT = "image:height"; 079 public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel"; 080 public static final String IMAGE_PROGRESSIVE = "image:progressive"; 081 public static final String IMAGE_NUMBER_OF_IMAGES = "image:numberOfImages"; 082 public static final String IMAGE_PHYSICAL_WIDTH_DPI = "image:physicalWidthDpi"; 083 public static final String IMAGE_PHYSICAL_HEIGHT_DPI = "image:physicalHeightDpi"; 084 public static final String IMAGE_PHYSICAL_WIDTH_INCHES = "image:physicalWidthInches"; 085 public static final String IMAGE_PHYSICAL_HEIGHT_INCHES = "image:physicalHeightInches"; 086 087 /** 088 * {@inheritDoc} 089 * 090 * @see StreamSequencer#sequence(InputStream, SequencerOutput, SequencerContext, ProgressMonitor) 091 */ 092 public void sequence( InputStream stream, 093 SequencerOutput output, 094 SequencerContext context, 095 ProgressMonitor progressMonitor ) { 096 progressMonitor.beginTask(10, ImageSequencerI18n.sequencerTaskName); 097 098 ImageMetadata metadata = new ImageMetadata(); 099 metadata.setInput(stream); 100 metadata.setDetermineImageNumber(true); 101 metadata.setCollectComments(true); 102 103 // Process the image stream and extract the metadata ... 104 if (!metadata.check()) { 105 metadata = null; 106 } 107 progressMonitor.worked(5); 108 if (progressMonitor.isCancelled()) return; 109 110 // Generate the output graph if we found useful metadata ... 111 if (metadata != null) { 112 NameFactory nameFactory = context.getFactories().getNameFactory(); 113 PathFactory pathFactory = context.getFactories().getPathFactory(); 114 Path metadataNode = pathFactory.create(METADATA_NODE); 115 116 // Place the image metadata into the output map ... 117 output.setProperty(metadataNode, nameFactory.create(IMAGE_PRIMARY_TYPE), "image:metadata"); 118 // output.psetProperty(metadataNode, nameFactory.create(IMAGE_MIXINS), ""); 119 output.setProperty(metadataNode, nameFactory.create(IMAGE_MIME_TYPE), metadata.getMimeType()); 120 // output.setProperty(metadataNode, nameFactory.create(IMAGE_ENCODING), ""); 121 output.setProperty(metadataNode, nameFactory.create(IMAGE_FORMAT_NAME), metadata.getFormatName()); 122 output.setProperty(metadataNode, nameFactory.create(IMAGE_WIDTH), metadata.getWidth()); 123 output.setProperty(metadataNode, nameFactory.create(IMAGE_HEIGHT), metadata.getHeight()); 124 output.setProperty(metadataNode, nameFactory.create(IMAGE_BITS_PER_PIXEL), metadata.getBitsPerPixel()); 125 output.setProperty(metadataNode, nameFactory.create(IMAGE_PROGRESSIVE), metadata.isProgressive()); 126 output.setProperty(metadataNode, nameFactory.create(IMAGE_NUMBER_OF_IMAGES), metadata.getNumberOfImages()); 127 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_WIDTH_DPI), metadata.getPhysicalWidthDpi()); 128 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_HEIGHT_DPI), metadata.getPhysicalHeightDpi()); 129 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_WIDTH_INCHES), metadata.getPhysicalWidthInch()); 130 output.setProperty(metadataNode, nameFactory.create(IMAGE_PHYSICAL_HEIGHT_INCHES), metadata.getPhysicalHeightInch()); 131 } 132 133 progressMonitor.done(); 134 } 135 }