package net.sourceforge.gpstools.xmp;
/* gpsdings
* Copyright (C) 2007 Moritz Ringler
* $Id: IIOXMPReader.java 441 2010-12-13 20:04:20Z ringler $
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.awt.Dimension;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.text.ParseException;
import javax.imageio.ImageReader;
import javax.imageio.ImageIO;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import org.w3c.dom.Node;
import net.sourceforge.gpstools.TrackInterpolator;
import net.sourceforge.gpstools.gpx.Wpt;
import net.sourceforge.gpstools.jpeg.JpegHeader;
/** A class that reads GPS data from XMP metadata in a jpeg header.
It uses Java Advanced Image I/O to retrieve the metadata.
<p>
Based on the 2005 Adobe XMP Specification.
@deprecated Advanced Image I/O is overkill here,
consider using {@link XMPReader} instead
**/
@Deprecated
public class IIOXMPReader extends AbstractXMPReader{
private transient ImageReader reader;
private final transient Object lockobj = new Object();
/** Allows some resources held by this object to be released.
* This method should be called whenever the reader
* might not be used any more. Do not rely on the finalizer
* releasing these resources. If this reader is used after calling
* this method the necessary resources will be reconstructed.
* @see ImageReader#dispose
*/
public void releaseResources(){
synchronized(lockobj){
if(reader != null){
reader.dispose();
reader = null;
}
}
}
/** Constructs a new IIOXMPReader. */
public IIOXMPReader(){
//sole constructor
}
@Override
public void finalize(){
if(reader != null){
reader.dispose();
}
}
@Override
protected ByteBuffer getXMPBytes(File jpeg) throws IOException{
return getXMPBytes(readMetadata(jpeg));
}
/** Looks for XMP metadata in the specified jpeg file and if successfull
* extracts image size information from it and returns it. This method
* reads the XMP properties tiff:ImageWidth, tiff:ImageHeight,
* exif:PixelXDimension, and exif:PixelYDimension.
* @return the image dimension in pixels or <code>null</code>
**/
@Override
public Dimension readJPEGDimension(File jpeg) throws IOException, ParseException{
IIOMetadata meta = readMetadata(jpeg);
Dimension result = (new JpegHeader(meta)).getImageDimension();
if(result == null){
ByteBuffer xmpBytes = getXMPBytes(meta);
CharBuffer xmpChars = getXMPChars(xmpBytes);
XMPProperties xmp = null;
if(xmpChars != null){
try{
xmp = parseXMP(xmpChars);
} catch (XMPReadException xrx){
throw xrx.toIOException();
}
result = (xmp == null)? null : xmp.getJpegDimension();
}
}
if(result != null && (result.width == 0 || result.height == 0)){
result = null;
}
return null;
}
/** Reads the metadata from the specified jpeg file. **/
protected IIOMetadata readMetadata(File jpeg) throws IOException{
synchronized(lockobj){
if(reader == null){
reader = ImageIO.getImageReadersByMIMEType("image/jpeg").next();
}
}
/* Read the input image meta-data */
InputStream is = new BufferedInputStream(new FileInputStream(jpeg));
ImageInputStream iis = ImageIO.createImageInputStream(is);
reader.setInput(iis);
IIOMetadata meta = reader.getImageMetadata(0);
iis.close();
is.close(); //must do this, otherwise file remains open
return meta;
}
/** Retrieves undecoded XMP data from meta.
* @return an XMP document or <code>null</null> if no XMP node was found
**/
protected static ByteBuffer getXMPBytes(IIOMetadata meta){
if(meta == null){
return null;
}
ByteBuffer result = null;
Node[] markers = new JpegHeader(meta).getApp1MarkerNodes();
for(Node m : markers){
if(m instanceof IIOMetadataNode){
IIOMetadataNode iiomn = (IIOMetadataNode) m;
Object o = iiomn.getUserObject();
if(o instanceof byte[]){
byte[] data = (byte[]) o;
if(data.length > 33){
String xmpnamespace = new String(data,0,28);
if(xmpnamespace.equals(XMP_NAMESPACE)){
result = ByteBuffer.wrap(data, 29, data.length-29);
break;
}
}
}
}
}
return result;
}
/** Main method for testing purposes. Tries to read XMP GPS tags from
all files handed as arguments. **/
public static void main(String[] argv) throws Exception{
IIOXMPReader me = new IIOXMPReader();
TrackInterpolator tp = new TrackInterpolator();
for(String f: argv){
Wpt pt = me.readGPSTag(new File(f));
if(pt != null){
tp.printWpt(System.out,pt);
}
}
me.releaseResources();
}
}