/*
* eXist Image Module Extension GetThumbnailsFunction
* Copyright (C) 2006-09 Adam Retter <adam.retter@devon.gov.uk>
* www.adamretter.co.uk
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.xquery.modules.image;
import org.apache.log4j.Logger;
import org.exist.external.org.apache.commons.io.output.ByteArrayOutputStream;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import org.exist.collections.Collection;
import org.exist.dom.BinaryDocument;
import org.exist.dom.DocumentImpl;
import org.exist.dom.QName;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.BasicFunction;
import org.exist.xquery.Cardinality;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.AnyURIValue;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.IntegerValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;
import org.exist.xquery.value.ValueSequence;
/**
*
*
* @author Rafael Troilo (rtroilo@gmail.com)
*
*/
public class GetThumbnailsFunction extends BasicFunction {
private static final Logger logger = Logger.getLogger(GetThumbnailsFunction.class);
private final static int MAXTHUMBHEIGHT = 100;
private final static int MAXTHUMBWIDTH = 100;
private final static String THUMBPREFIX = "";
private final static String THUMBPATH = "thumbs";
public final static FunctionSignature signature = new FunctionSignature(
new QName("thumbnail", ImageModule.NAMESPACE_URI,
ImageModule.PREFIX),
"Generate thumbnails from the given database collection",
new SequenceType[] {
new FunctionParameterSequenceType("collection", Type.ANY_URI, Cardinality.EXACTLY_ONE, "The URI to the collection"),
new FunctionParameterSequenceType("thumbnail-location", Type.ANY_URI, Cardinality.ZERO_OR_ONE, "The location in the database where the thumbnails should be created, this can be a local path, with the prefix 'xmldb:' a absolute path within the database or with 'rel:' path relative to the given $collection. You can leave this empty then the default is 'rel:/thumbs'. "),
new FunctionParameterSequenceType("dimension", Type.INTEGER, Cardinality.ZERO_OR_MORE, "The dimension of the thumbnails, if empty then the default values are 'maxheight = 100' and 'maxwidth = 100', the first value is 'maxheight' and the second 'maxwidth'. "),
new FunctionParameterSequenceType("prefix", Type.STRING, Cardinality.ZERO_OR_ONE, "The prefix to append to the thumbnail filenames") },
new FunctionReturnSequenceType(Type.STRING, Cardinality.ZERO_OR_MORE, "the result"));
public GetThumbnailsFunction(XQueryContext context) {
super(context, signature);
}
/*
* (non-Javadoc)
*
* @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[],
* org.exist.xquery.value.Sequence)
*/
public Sequence eval(Sequence[] args, Sequence contextSequence)
throws XPathException {
ValueSequence result = new ValueSequence();
// boolean isDatabasePath = false;
boolean isSaveToDataBase = false;
if (args[0].isEmpty()) {
return Sequence.EMPTY_SEQUENCE;
}
AnyURIValue picturePath = (AnyURIValue) args[0].itemAt(0);
if (picturePath.getStringValue().startsWith("xmldb:exist://")) {
picturePath = new AnyURIValue(picturePath.getStringValue()
.substring(14));
}
AnyURIValue thumbPath = null;
if (args[1].isEmpty()) {
thumbPath = new AnyURIValue(picturePath.toXmldbURI().append(
THUMBPATH));
isSaveToDataBase = true;
} else {
thumbPath = (AnyURIValue) args[1].itemAt(0);
if (thumbPath.getStringValue().startsWith("file:")) {
isSaveToDataBase = false;
thumbPath = new AnyURIValue(thumbPath.getStringValue().substring(5));
} else {
isSaveToDataBase = true;
try {
XmldbURI thumbsURI = XmldbURI.xmldbUriFor(thumbPath.getStringValue());
if (!thumbsURI.isAbsolute())
thumbsURI = picturePath.toXmldbURI().append(thumbPath.toString());
thumbPath = new AnyURIValue(thumbsURI.toString());
} catch (URISyntaxException e) {
throw new XPathException(this, e.getMessage());
}
}
}
// result.add(new StringValue(picturePath.getStringValue()));
// result.add(new StringValue(thumbPath.getStringValue() + " isDB?= "
// + isSaveToDataBase));
int maxThumbHeight = MAXTHUMBHEIGHT;
int maxThumbWidth = MAXTHUMBWIDTH;
if (!args[2].isEmpty()) {
maxThumbHeight = ((IntegerValue) args[2].itemAt(0)).getInt();
if (args[2].hasMany())
maxThumbWidth = ((IntegerValue) args[2].itemAt(1)).getInt();
}
String prefix = THUMBPREFIX;
if (!args[3].isEmpty()) {
prefix = args[3].itemAt(0).getStringValue();
}
// result.add(new StringValue("maxThumbHeight = " + maxThumbHeight
// + ", maxThumbWidth = " + maxThumbWidth));
DBBroker dbbroker = context.getBroker();
BrokerPool pool = null;
try {
pool = BrokerPool.getInstance();
} catch (Exception e) {
result.add(new StringValue(e.getMessage()));
return result;
}
TransactionManager transact = pool.getTransactionManager();
// Start transaction
Txn transaction = transact.beginTransaction();
Collection thumbCollection = null;
File thumbDir = null;
if (isSaveToDataBase) {
try {
thumbCollection = dbbroker.getOrCreateCollection(transaction,
thumbPath.toXmldbURI());
dbbroker.saveCollection(transaction, thumbCollection);
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
} else {
thumbDir = new File(thumbPath.toString());
if (!thumbDir.isDirectory())
try {
thumbDir.mkdirs();
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
}
Collection allPictures = dbbroker.getCollection(picturePath
.toXmldbURI());
if (allPictures == null) {
return Sequence.EMPTY_SEQUENCE;
}
Collection existingThumbsCol = null;
File[] existingThumbsArray = null;
if (isSaveToDataBase) {
existingThumbsCol = dbbroker.getCollection(thumbPath.toXmldbURI());
} else {
existingThumbsArray = thumbDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return (name.endsWith(".jpeg") || name.endsWith(".jpg"));
}
});
}
DocumentImpl docImage = null;
BinaryDocument binImage = null;
BinaryDocument doc = null;
BufferedImage bImage = null;
byte[] imgData = null;
Image image = null;
ByteArrayOutputStream os = null;
Iterator i = allPictures.iterator(dbbroker);
while (i.hasNext()) {
docImage = (DocumentImpl) i.next();
// is not already existing??
if (!((fileExist(existingThumbsCol, docImage, prefix)) || (fileExist(
existingThumbsArray, docImage, prefix)))) {
if (docImage.getResourceType() == DocumentImpl.BINARY_FILE)
// TODO maybe extends for gifs too.
if (docImage.getMetadata().getMimeType().startsWith(
"image/jpeg")) {
binImage = (BinaryDocument) docImage;
// get a byte array representing the image
try {
InputStream is = dbbroker.getBinaryResource(binImage);
image = ImageIO.read(is);
} catch (IOException ioe) {
throw new XPathException(this,ioe.getMessage());
}
try {
bImage = ImageModule.createThumb(image, maxThumbHeight,
maxThumbWidth);
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
if (isSaveToDataBase) {
os = new ByteArrayOutputStream();
try {
ImageIO.write(bImage, "jpg", os);
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
try {
doc = thumbCollection.addBinaryResource(
transaction, dbbroker,
XmldbURI.create(prefix
+ docImage.getFileURI()), os
.toByteArray(), "image/jpeg");
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
// result.add(new
// StringValue(""+docImage.getFileURI()+"|"+thumbCollection.getURI()+THUMBPREFIX
// + docImage.getFileURI()));
} else {
try {
ImageIO
.write(
bImage,
"jpg",
new File(thumbPath.toString()
+ "/" + prefix
+ docImage.getFileURI()));
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
// result.add(new StringValue(
// thumbPath.toString() + "/"
// + THUMBPREFIX
// + docImage.getFileURI()));
}
}
} else {
// result.add(new StringValue(""+docImage.getURI()+"|"
// + ((existingThumbsCol != null) ? ""
// + existingThumbsCol.getURI() : thumbDir
// .toString()) + "/" + prefix
// + docImage.getFileURI()));
result.add(new StringValue(docImage.getFileURI().toString()));
}
}
try {
transact.commit(transaction);
} catch (Exception e) {
throw new XPathException(this, e.getMessage());
}
transact.getJournal().flushToLog(true);
dbbroker.closeDocument();
return result;
}
private boolean fileExist(Collection col, DocumentImpl file, String prefix) {
if (col != null)
return col.hasDocument(XmldbURI.create(prefix + file.getFileURI()));
return false;
}
private boolean fileExist(File[] col, DocumentImpl file, String prefix) {
if (col != null)
for (int i = 0; i < col.length; i++) {
if (col[i].getName().endsWith(prefix + file.getFileURI())) {
return true;
}
}
return false;
}
}