changeset 500:b2325b33b77b digilibPDF

completely restructured the scaler
author cmielack
date Mon, 23 Feb 2009 14:45:22 +0100
parents 794a9f25f15c
children d960b7c68b08
files servlet/src/digilib/servlet/DigilibImageWorker.java servlet/src/digilib/servlet/DigilibPDFWorker.java servlet/src/digilib/servlet/ImageJobInformation.java servlet/src/digilib/servlet/PDFCache.java servlet/src/digilib/servlet/PDFJobDeclaration.java servlet/src/digilib/servlet/PDFMaker.java servlet/src/digilib/servlet/RequestHandler.java servlet/src/digilib/servlet/Scaler.java
diffstat 8 files changed, 2595 insertions(+), 349 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/DigilibImageWorker.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,295 @@
+/* DigilibImageWorker.java -- worker for image operations
+ * 
+ * Digital Image Library servlet components
+ * 
+ * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de)
+ * 
+ * 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 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * Please read license.txt for the full details. A copy of the GPL may be found
+ * at http://www.gnu.org/copyleft/lgpl.html
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ *  
+ * Created on 19.10.2004
+ */
+
+package digilib.servlet;
+
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import digilib.image.DocuImage;
+import digilib.image.ImageOpException;
+import digilib.image.ImageOps;
+import digilib.io.FileOpException;
+import digilib.io.ImageFile;
+
+/**
+ * worker for image operations.
+ * 
+ * @author casties
+ * 
+ */
+public class DigilibImageWorker extends DigilibWorker {
+
+	private DigilibConfiguration dlConfig;
+
+	//HttpServletResponse response;
+
+	OutputStream outstream;
+	
+	long startTime;
+
+	String mimeType;
+
+	int scaleQual;
+
+	//DigilibRequest dlRequest;
+
+	//ImageJobInformation ijd;
+	
+	float paramROT;
+
+	float paramCONT;
+
+	float paramBRGT;
+
+	float[] paramRGBM;
+
+	float[] paramRGBA;
+
+	ImageFile fileToLoad;
+
+	float areaXoff;
+
+	float areaYoff;
+
+	float areaWidth;
+
+	float areaHeight;
+
+	float scaleXY;
+
+	Rectangle2D outerUserImgArea;
+
+	Rectangle2D innerUserImgArea;
+
+	float minSubsample;
+
+	boolean wholeRotArea;
+
+	boolean vmir;
+	boolean hmir;
+	
+	int forceType;
+
+	/**
+	 * @param dlConfig
+	 * @param response
+	 * @param mimeType
+	 * @param scaleQual
+	 * @param dlRequest
+	 * @param paramROT
+	 * @param paramCONT
+	 * @param paramBRGT
+	 * @param paramRGBM
+	 * @param paramRGBA
+	 * @param fileToLoad
+	 * @param areaXoff
+	 * @param outerUserImgArea
+	 * @param innerUserImgArea
+	 * @param minSubsample
+	 * @param wholeRotArea
+	 * @param forceType
+	 */
+	public DigilibImageWorker(DigilibConfiguration dlConfig,
+			OutputStream outstream, String mimeType, int scaleQual,
+			//DigilibRequest dlRequest, 
+			//ImageJobInformation ijd,
+			float paramROT, float paramCONT,
+			float paramBRGT, float[] paramRGBM, float[] paramRGBA,
+			ImageFile fileToLoad, float scaleXY, Rectangle2D outerUserImgArea,
+			Rectangle2D innerUserImgArea, float minSubsample,
+			boolean wholeRotArea, int forceType, boolean hmir, boolean vmir) {
+		super();
+		this.dlConfig = dlConfig;
+		this.outstream = outstream;
+		this.mimeType = mimeType;
+		this.scaleQual = scaleQual;
+		//this.dlRequest = dlRequest;
+		//this.ijd = ijd;
+		this.paramROT = paramROT;
+		this.paramCONT = paramCONT;
+		this.paramBRGT = paramBRGT;
+		this.paramRGBM = paramRGBM;
+		this.paramRGBA = paramRGBA;
+		this.fileToLoad = fileToLoad;
+		this.scaleXY = scaleXY;
+		this.outerUserImgArea = outerUserImgArea;
+		this.innerUserImgArea = innerUserImgArea;
+		this.minSubsample = minSubsample;
+		this.wholeRotArea = wholeRotArea;
+		this.forceType = forceType;
+		this.hmir = hmir;
+		this.vmir = vmir;
+	}
+
+	/*
+	 * do the work
+	 */
+	public DocuImage render() throws FileOpException, IOException, ImageOpException {
+		;
+		logger.debug("image worker " + this.getName() + " working");
+		startTime = System.currentTimeMillis();
+
+		/* crop and scale image */
+
+		// new DocuImage instance
+		DocuImage docuImage = dlConfig.getDocuImageInstance();
+		if (docuImage == null) {
+			throw new ImageOpException("Unable to load DocuImage class!");
+		}
+
+		// set interpolation quality
+		docuImage.setQuality(scaleQual);
+
+		Rectangle loadRect = outerUserImgArea.getBounds();
+		// use subimage loading if possible
+		if (docuImage.isSubimageSupported()) {
+			logger.debug("Subimage: scale " + scaleXY + " = " + (1 / scaleXY));
+			float subf = 1f;
+			float subsamp = 1f;
+			if (scaleXY < 1) {
+				subf = 1 / scaleXY;
+				// for higher quality reduce subsample factor by
+				// minSubsample
+				if (scaleQual > 0) {
+					subsamp = (float) Math.max(Math.floor(subf / minSubsample),
+							1d);
+				} else {
+					subsamp = (float) Math.floor(subf);
+				}
+				scaleXY = subsamp / subf;
+				logger.debug("Using subsampling: " + subsamp + " rest "
+						+ scaleXY);
+			}
+
+			docuImage.loadSubimage(fileToLoad, loadRect, (int) subsamp);
+
+			logger.debug("SUBSAMP: " + subsamp + " -> " + docuImage.getWidth()
+					+ "x" + docuImage.getHeight());
+
+			docuImage.scale(scaleXY, scaleXY);
+
+		} else {
+			// else load and crop the whole file
+			docuImage.loadImage(fileToLoad);
+			docuImage.crop((int) loadRect.getX(), (int) loadRect.getY(),
+					(int) loadRect.getWidth(), (int) loadRect.getHeight());
+
+			docuImage.scale(scaleXY, scaleXY);
+		}
+
+		// mirror image
+		// operation mode: "hmir": mirror horizontally, "vmir": mirror
+		// vertically
+		if (hmir) {
+			docuImage.mirror(0);
+		}
+		if (vmir) {
+			docuImage.mirror(90);
+		}
+
+		// rotate image
+		if (paramROT != 0d) {
+			docuImage.rotate(paramROT);
+			if (wholeRotArea) {
+				// crop to the inner bounding box
+				float xcrop = (float) (docuImage.getWidth() - innerUserImgArea
+						.getWidth()
+						* scaleXY);
+				float ycrop = (float) (docuImage.getHeight() - innerUserImgArea
+						.getHeight()
+						* scaleXY);
+				if ((xcrop > 0) || (ycrop > 0)) {
+					// only crop smaller
+					xcrop = (xcrop > 0) ? xcrop : 0;
+					ycrop = (ycrop > 0) ? ycrop : 0;
+					// crop image
+					docuImage.crop((int) (xcrop / 2), (int) (ycrop / 2),
+							(int) (docuImage.getWidth() - xcrop),
+							(int) (docuImage.getHeight() - ycrop));
+				}
+			}
+
+		}
+
+		// color modification
+		if ((paramRGBM != null) || (paramRGBA != null)) {
+			// make shure we actually have two arrays
+			if (paramRGBM == null) {
+				paramRGBM = new float[3];
+			}
+			if (paramRGBA == null) {
+				paramRGBA = new float[3];
+			}
+			// calculate "contrast" values (c=2^x)
+			float[] mult = new float[3];
+			for (int i = 0; i < 3; i++) {
+				mult[i] = (float) Math.pow(2, (float) paramRGBM[i]);
+			}
+			docuImage.enhanceRGB(mult, paramRGBA);
+		}
+
+		// contrast and brightness enhancement
+		if ((paramCONT != 0f) || (paramBRGT != 0f)) {
+			float mult = (float) Math.pow(2, paramCONT);
+			docuImage.enhance(mult, paramBRGT);
+		}
+
+		logger.debug("rendered in " + (System.currentTimeMillis() - startTime) + "ms");
+
+		return docuImage;
+	}
+
+	public void write(DocuImage img) throws FileOpException, IOException {
+		/* write the resulting image */
+
+		// setup output -- if output type is forced use that otherwise
+		// if source is JPG then dest will be JPG else it's PNG
+		if (forceType != ImageOps.TYPE_AUTO) {
+			if (forceType == ImageOps.TYPE_JPEG) {
+				mimeType = "image/jpeg";
+			}
+			if (forceType == ImageOps.TYPE_PNG) {
+				mimeType = "image/png";
+			}
+		} else if ((mimeType.equals("image/jpeg")
+				|| mimeType.equals("image/jp2") || mimeType.equals("image/fpx"))) {
+			mimeType = "image/jpeg";
+		} else {
+			mimeType = "image/png";
+		}
+
+		// write the image
+		img.writeImage(mimeType, outstream);
+		outstream.flush();
+		
+		
+		logger.info("image worker " + this.getName() + " done in "
+				+ (System.currentTimeMillis() - startTime));
+
+		img.dispose();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/DigilibPDFWorker.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,227 @@
+/* DigilibImageWorker.java -- worker for image operations
+ * 
+ * Digital Image Library servlet components
+ * 
+ * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de)
+ * 
+ * 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 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * Please read license.txt for the full details. A copy of the GPL may be found
+ * at http://www.gnu.org/copyleft/lgpl.html
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ *  
+ * Created on 19.10.2004
+ */
+
+package digilib.servlet;
+
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import com.lowagie.text.BadElementException;
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Image;
+import com.lowagie.text.PageSize;
+
+import digilib.image.DocuImage;
+import digilib.image.DocuImageImpl;
+import digilib.image.ImageLoaderDocuImage;
+import digilib.image.ImageOpException;
+import digilib.image.ImageOps;
+import digilib.servlet.DigilibImageWorker;
+import digilib.io.FileOpException;
+import digilib.io.ImageFile;
+
+/**
+ * worker for image operations.
+ * 
+ * @author casties
+ * 
+ */
+public class DigilibPDFWorker extends DigilibImageWorker {
+
+	private DigilibConfiguration dlConfig;
+
+
+	long startTime;
+
+	String mimeType;
+
+	int scaleQual;
+
+	DigilibRequest dlRequest;
+
+	float paramROT;
+
+	float paramCONT;
+
+	float paramBRGT;
+
+	float[] paramRGBM;
+
+	float[] paramRGBA;
+
+	ImageFile fileToLoad;
+
+	float areaXoff;
+
+	float areaYoff;
+
+	float areaWidth;
+
+	float areaHeight;
+
+	float scaleXY;
+
+	Rectangle2D outerUserImgArea;
+
+	Rectangle2D innerUserImgArea;
+
+	float minSubsample;
+
+	boolean wholeRotArea;
+
+	int forceType;
+	
+	Document doc;
+	
+	
+	//public ImageLoaderDocuImage render();// throws Exception;
+
+
+	/**
+	 * @param dlConfig
+	 * @param response
+	 * @param mimeType
+	 * @param scaleQual
+	 * @param dlRequest
+	 * @param paramROT
+	 * @param paramCONT
+	 * @param paramBRGT
+	 * @param paramRGBM
+	 * @param paramRGBA
+	 * @param fileToLoad
+	 * @param areaXoff
+	 * @param outerUserImgArea
+	 * @param innerUserImgArea
+	 * @param minSubsample
+	 * @param wholeRotArea
+	 * @param forceType
+	 */
+	public DigilibPDFWorker(DigilibConfiguration dlConfig,
+			BufferedOutputStream outstream, String mimeType, int scaleQual,
+			DigilibRequest dlRequest, float paramCONT,
+			float paramBRGT, float[] paramRGBM, float[] paramRGBA,
+			ImageFile fileToLoad, float scaleXY, Rectangle2D outerUserImgArea,
+			Rectangle2D innerUserImgArea, float minSubsample,
+			boolean wholeRotArea, int forceType, Document doc) {
+		super(dlConfig,
+				outstream, mimeType, scaleQual,
+				dlRequest, 0.0f , paramCONT,
+				paramBRGT, paramRGBM,paramRGBA,
+				fileToLoad, scaleXY, outerUserImgArea,
+				innerUserImgArea,minSubsample,
+				wholeRotArea, forceType);
+		
+		this.dlConfig = dlConfig;
+		this.outstream = outstream;
+		this.mimeType = mimeType;
+		this.scaleQual = scaleQual;
+		this.dlRequest = dlRequest;
+		//this.paramROT = paramROT;
+		this.paramCONT = paramCONT;
+		this.paramBRGT = paramBRGT;
+		this.paramRGBM = paramRGBM;
+		this.paramRGBA = paramRGBA;
+		this.fileToLoad = fileToLoad;
+		this.scaleXY = scaleXY;
+		this.outerUserImgArea = outerUserImgArea;
+		this.innerUserImgArea = innerUserImgArea;
+		this.minSubsample = minSubsample;
+		this.wholeRotArea = wholeRotArea;
+		this.forceType = forceType;
+		this.doc = doc;
+	}
+
+	public void run() {
+		//logger.debug((++waitingThreads) + " waiting threads");
+		ImageLoaderDocuImage img = null;
+		try {
+			sem.acquire();
+			//waitingThreads--;
+		} catch (InterruptedException e) {
+			error = e;
+			//waitingThreads--;
+			// should we reinterrupt?
+			return;
+		}
+		//logger.debug((++runningThreads) + " running threads");
+		try {
+			/* 
+			 * do rendering under the semaphore 
+			 */
+			img = (ImageLoaderDocuImage) super.render();
+		} catch (Throwable e) {
+			error = e;
+			logger.error(e);
+		} finally {
+		//	runningThreads--;
+			sem.release();
+		}
+		/* 
+		 * write the result without semaphore
+		 */
+		if (!hasError()) {
+			try{
+				write(img);
+			} catch (Throwable e) {
+				error = e;
+				logger.error(e);
+			}
+		}
+	}
+
+	public void write(ImageLoaderDocuImage img) throws FileOpException, IOException {
+		/* write the resulting image */
+
+
+		try {
+			long timing = System.currentTimeMillis();
+			Image theimg = Image.getInstance(img.getImage(),null);
+			
+			theimg.scaleToFit(PageSize.A4.getWidth(),PageSize.A4.getHeight());
+			
+			logger.debug(" --- loading and scaling took "+(-timing+System.currentTimeMillis())+"ms");
+
+			timing = System.currentTimeMillis();
+			
+			doc.add(theimg);
+			logger.debug(" --- adding took "+(-timing+System.currentTimeMillis())+"ms");
+
+		} catch (BadElementException e) {
+			e.printStackTrace();
+			logger.debug("------DigilibPDFWorker write BadElementException");
+
+		} catch (DocumentException e) {
+			e.printStackTrace();
+			logger.debug("------DigilibPDFWorker write DocumentException");
+
+		}
+		
+		logger.info("pdf worker " + this.getName() + " done in "
+				+ (System.currentTimeMillis() - startTime));
+
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/ImageJobInformation.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,620 @@
+package digilib.servlet;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.log4j.Logger;
+
+import digilib.image.ImageOpException;
+import digilib.image.ImageOps;
+import digilib.image.ImageSize;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirent;
+import digilib.io.FileOpException;
+import digilib.io.FileOps;
+import digilib.io.ImageFile;
+import digilib.io.ImageFileset;
+
+
+/** 
+ * A container class for storing a set of instructional parameters 
+ * used for content generating classes like MakePDF.  
+ * 
+ * TODO aufraeumen, zwischenwerte in der ParameterMap cachen
+ * 
+ * @author cmielack
+ *
+ */
+
+public class ImageJobInformation extends ParameterMap {
+	
+	String[] parameter_list = {"fn","pn","dw","dh",
+								"wx", "wy", "ww", "wh", "ws", 
+								"mo", "rot", "cont", "brgt", "rgbm", "rbgm", 
+								"ddpi", "ddpix", "ddpiy", "scale"};
+	DigilibConfiguration dlConfig = null;
+	protected static Logger logger = Logger.getLogger("digilib.servlet");
+
+	ImageFile fileToLoad = null;
+	ImageFileset fileset=null;
+	String FilePath = null;
+	ImageSize expectedSourceSize = null;
+	Float scaleXY = null;
+	Rectangle2D userImgArea = null;
+	Rectangle2D outerUserImgArea= null;
+	
+//	Integer paramDW = null;
+//	Integer paramDH 
+	public ImageJobInformation() {
+		super(30);
+		
+		// url of the page/document (second part)
+		newParameter("fn", "", null, 's');
+		// page number
+		newParameter("pn", new Integer(1), null, 's');
+		// width of client in pixels
+		newParameter("dw", new Integer(0), null, 's');
+		// height of client in pixels
+		newParameter("dh", new Integer(0), null, 's');
+		// left edge of image (float from 0 to 1)
+		newParameter("wx", new Float(0), null, 's');
+		// top edge in image (float from 0 to 1)
+		newParameter("wy", new Float(0), null, 's');
+		// width of image (float from 0 to 1)
+		newParameter("ww", new Float(1), null, 's');
+		// height of image (float from 0 to 1)
+		newParameter("wh", new Float(1), null, 's');
+		// scale factor
+		newParameter("ws", new Float(1), null, 's');
+		// special options like 'fit' for gifs
+		newParameter("mo", "", null, 's');
+		// rotation angle (degree)
+		newParameter("rot", new Float(0), null, 's');
+		// contrast enhancement factor
+		newParameter("cont", new Float(0), null, 's');
+		// brightness enhancement factor
+		newParameter("brgt", new Float(0), null, 's');
+		// color multiplicative factors
+		newParameter("rgbm", "0/0/0", null, 's');
+		// color additive factors
+		newParameter("rgba", "0/0/0", null, 's');
+		// display dpi resolution (total)
+		newParameter("ddpi", new Float(0), null, 's');
+		// display dpi X resolution
+		newParameter("ddpix", new Float(0), null, 's');
+		// display dpi Y resolution
+		newParameter("ddpiy", new Float(0), null, 's');
+		// scale factor for mo=ascale
+		newParameter("scale", new Float(1), null, 's');
+
+		/*
+		 * Parameters of type 'i' are not exchanged between client and server,
+		 * but are for the servlets or JSPs internal use.
+		 */
+
+		// url of the page/document (first part, may be empty)
+		newParameter("request.path", "", null, 'i');
+		// base URL (from http:// to below /servlet)
+		newParameter("base.url", null, null, 'i');
+
+		/*
+		 * Parameters of type 'c' are for the clients use
+		 */
+/*
+		// "real" filename
+		newParameter("img.fn", "", null, 'c');
+		// image dpi x
+		newParameter("img.dpix", new Integer(0), null, 'c');
+		// image dpi y
+		newParameter("img.dpiy", new Integer(0), null, 'c');
+		// hires image size x
+		newParameter("img.pix_x", new Integer(0), null, 'c');
+		// hires image size y
+		newParameter("img.pix_y", new Integer(0), null, 'c');
+		// total number of pages
+		newParameter("pt", new Integer(0), null, 'c');
+		// display level of digilib (0 = just image, 1 = one HTML page
+		// 2 = in frameset, 3 = XUL-'frameset'
+		// 4 = XUL-Sidebar )
+		newParameter("lv", new Integer(2), null, 'c');
+		// marks
+		newParameter("mk", "", null, 'c');
+*/	
+	}
+
+	public void setConfig(DigilibConfiguration dlcfg){
+		dlConfig = dlcfg;
+	}
+	
+	public void setWithRequest(HttpServletRequest request) {
+		for (String param : parameter_list){
+			if (request.getParameterMap().containsKey(param)){
+				//request.get
+//				put(param, request.getParameter(param));
+				this.setValueFromString(param, request.getParameter(param));
+			}
+		}
+	setValueFromString("request.path", ((HttpServletRequest) request).getPathInfo());
+
+	}
+	
+	public String[] getParameterList(){
+		return parameter_list;
+	}
+	
+
+	public boolean hasOption(String param, String opt) {
+		String s = getAsString(param);
+		if (s != null) {
+			StringTokenizer i = new StringTokenizer(s, ",");
+			while (i.hasMoreTokens()) {
+				if (i.nextToken().equals(opt)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
+	
+	/**
+	 * evaluate request data in order to gain the parameters for the image worker
+	 * 
+	 * @throws ImageOpException 
+	 * @throws IOException 
+	 * */
+
+	public String get_mimeType() throws IOException, ImageOpException{
+		String mimeType = "image/png";
+		ImageFile fileToLoad = get_fileToLoad();
+		if(fileToLoad != null)
+			mimeType = fileToLoad.getMimetype();
+		return mimeType;
+	}
+	
+	public ImageFile get_fileToLoad() throws IOException, ImageOpException{
+		
+		//logger.debug("get_fileToLoad()");
+		if(fileToLoad == null){
+			ImageFileset fileset = get_fileset();
+			
+			/* select a resolution */
+			if (get_hiresOnly()) {
+				// get first element (= highest resolution)
+				fileToLoad = fileset.getBiggest();
+			} else if (get_loresOnly()) {
+				// enforced lores uses next smaller resolution
+				fileToLoad = fileset.getNextSmaller(get_expectedSourceSize());
+				if (fileToLoad == null) {
+					// this is the smallest we have
+					fileToLoad = fileset.getSmallest();
+				}
+			} else {
+				// autores: use next higher resolution
+				fileToLoad = fileset.getNextBigger(get_expectedSourceSize());
+				if (fileToLoad == null) {
+					// this is the highest we have
+					fileToLoad = fileset.getBiggest();
+				}
+			}
+			logger.info("Planning to load: " + fileToLoad.getFile());
+		}
+		
+		return fileToLoad;
+
+	}
+	
+	public ImageFileset get_fileset() throws FileOpException{
+		//logger.debug("get_fileset()");
+		if(fileset==null){
+			DocuDirCache dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+	
+			fileset = (ImageFileset) dirCache.getFile(getFilePath(), getAsInt("pn"), FileOps.CLASS_IMAGE);
+			if (fileset == null) {
+				throw new FileOpException("File " + getFilePath() + "("
+						+ getAsInt("pn") + ") not found.");
+			}
+		}
+		return fileset;
+	}
+	
+	public String getFilePath() {
+		//logger.debug("getFilePath()");
+		if(FilePath == null){
+			String s = this.getAsString("request.path");
+			s += this.getAsString("fn");
+			FilePath = FileOps.normalName(s);
+		}
+		return FilePath;
+	}
+
+	public boolean get_hiresOnly(){
+		//logger.debug("get_hiresOnly()");
+		return hasOption("mo","clip") || hasOption("mo","osize") || hasOption("mo","hires");
+	}
+	
+	public boolean get_loresOnly(){
+		//logger.debug("get_loresOnly()");
+
+		return hasOption("mo","lores");
+	}
+
+	public boolean get_scaleToFit() {
+		//logger.debug("get_scaleToFit()");
+
+		return !(hasOption("mo","clip") || hasOption("mo","osize") || hasOption("mo","ascale"));
+	}
+
+	public boolean get_absoluteScale(){
+		//logger.debug("get_absoluteScale()");
+
+		return hasOption("mo","osize") || hasOption("mo","ascale");
+	}
+	
+	
+	public ImageSize get_expectedSourceSize() throws IOException, ImageOpException{
+		//logger.debug("get_expectedSourceSize()");
+
+		if (expectedSourceSize == null){
+			expectedSourceSize = new ImageSize();
+			
+			if (get_scaleToFit()) {
+				// scale to fit -- calculate minimum source size
+				float scale = (1 / Math.min(getAsFloat("ww"), getAsFloat("wh"))) * getAsFloat("ws");
+				expectedSourceSize.setSize((int) (getAsInt("dw") * scale),
+						(int) (getAsInt("dh") * scale));
+			} else if (get_absoluteScale() && hasOption("mo", "ascale")) {
+				// absolute scale -- apply scale to hires size
+				expectedSourceSize = get_hiresSize().getScaled(getAsFloat("scale"));
+			} else {
+				// clip to fit -- source = destination size
+				expectedSourceSize.setSize((int) (getAsInt("dw") * getAsFloat("ws")),
+						(int) (getAsInt("dh") * getAsFloat("ws")));
+			}
+		}
+		return expectedSourceSize;
+	}
+	
+	public ImageSize get_hiresSize() throws IOException, ImageOpException{
+		logger.debug("get_hiresSize()");
+
+		ImageSize hiresSize = null;
+		ImageFileset fileset = get_fileset();
+		if (get_absoluteScale()) {
+			ImageFile hiresFile = fileset.getBiggest();
+			if (!hiresFile.isChecked()) {
+				ImageOps.checkFile(hiresFile);
+			}
+			hiresSize = hiresFile.getSize();
+			
+			/* prepare resolution and scale factor for original size */
+			if (hasOption("mo", "osize")) {
+				// get original resolution from metadata
+				fileset.checkMeta();
+				float origResX = fileset.getResX();
+				float origResY = fileset.getResY();
+				if ((origResX == 0) || (origResY == 0)) {
+					throw new ImageOpException("Missing image DPI information!");
+				}
+
+				if ((getAsFloat("ddpix") == 0) || (getAsFloat("ddpiy") == 0)) {
+					throw new ImageOpException(
+							"Missing display DPI information!");
+				}
+				// calculate absolute scale factor
+				float sx = getAsFloat("ddpix") / origResX;
+				float sy = getAsFloat("ddpiy") / origResY;
+				// currently only same scale :-(
+				put("scale", (sx + sy)/2f);
+			}
+			
+		}
+		return hiresSize;
+		
+	}
+	
+	public float get_scaleXY() throws IOException, ImageOpException{
+		//logger.debug("get_scaleXY()");
+		if(scaleXY == null){
+			// coordinates and scaling
+			float areaWidth;
+			float areaHeight;
+			float scaleX;
+			float scaleY;
+			ImageSize imgSize = get_fileToLoad().getSize();
+			ImageSize hiresSize = get_hiresSize();
+			// coordinates using Java2D
+			// image size in pixels
+	//		Rectangle2D imgBounds = new Rectangle2D.Float(0, 0, imgSize
+	//				.getWidth(), imgSize.getHeight());
+			// user window area in [0,1] coordinates
+			Rectangle2D relUserArea = new Rectangle2D.Float(getAsFloat("wx"), getAsFloat("wy"),
+					getAsFloat("ww"), getAsFloat("wh"));
+			// transform from relative [0,1] to image coordinates.
+			AffineTransform imgTrafo = AffineTransform.getScaleInstance(imgSize
+					.getWidth(), imgSize.getHeight());
+			// transform user coordinate area to image coordinate area
+			Rectangle2D userImgArea = imgTrafo.createTransformedShape(
+					relUserArea).getBounds2D();
+	
+			//##########################################################################################################################
+			// calculate scaling factors based on inner user area
+			if (get_scaleToFit()) {
+				areaWidth = (float) userImgArea.getWidth();
+				areaHeight = (float) userImgArea.getHeight();
+				scaleX = get_paramDW() / areaWidth * getAsFloat("ws");
+				scaleY = get_paramDH() / areaHeight * getAsFloat("ws");
+				scaleXY = (scaleX > scaleY) ? scaleY : scaleX;
+			} else if (get_absoluteScale()) {
+				scaleXY = getAsFloat("scale");
+				// we need to correct the factor if we use a pre-scaled image
+				if (imgSize.getWidth() != hiresSize.getWidth()) {
+					scaleXY *= (float)hiresSize.getWidth() / (float)imgSize.getWidth();
+				}
+				//scaleX = scaleXY;
+				//scaleY = scaleXY;
+				areaWidth = get_paramDW() / scaleXY * getAsFloat("ws");
+				areaHeight = get_paramDH() / scaleXY * getAsFloat("ws");
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+			} else {
+				// crop to fit
+				areaWidth = get_paramDW() * getAsFloat("ws");
+				areaHeight = get_paramDH() * getAsFloat("ws");
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+				scaleX = 1f;
+				scaleY = 1f;
+				scaleXY = 1f;
+			}
+		}
+		
+		return (float) scaleXY;
+	}
+	
+	public int get_paramDW(){
+		logger.debug("get_paramDW()");
+
+		int paramDW = getAsInt("dw");
+		int paramDH = getAsInt("dh");
+		
+		float imgAspect;
+		try {
+			imgAspect = get_fileToLoad().getAspect();
+			if (paramDW == 0) {
+				paramDW = (int) Math.round(paramDH * imgAspect);
+				setValue("dw", paramDW);
+			} else if (paramDH == 0) {
+				setValue("dh",  (int) Math.round(paramDW / imgAspect));
+			}
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (ImageOpException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return paramDW;
+	}
+	
+	public int get_paramDH(){
+		logger.debug("get_paramDH()");
+
+		int paramDW = getAsInt("dw");
+		int paramDH = getAsInt("dh");
+		
+		float imgAspect;
+		try {
+			imgAspect = get_fileToLoad().getAspect();
+			if (paramDW == 0) {
+				setValue("dw", (int) Math.round(paramDH * imgAspect));
+			} else if (paramDH == 0) {
+				paramDH = (int) Math.round(paramDW / imgAspect);
+				setValue("dh", paramDH );
+			}
+
+
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (ImageOpException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return paramDH;
+	}
+	public Integer get_scaleQual(){
+		logger.debug("get_scaleQual()");
+
+		Integer qual = 	dlConfig.getAsInt("default-quality");
+		if(hasOption("mo","q0"))
+			qual = 0;
+		else if(hasOption("mo","q1"))
+			qual = 1;
+		else if(hasOption("mo","q2"))
+			qual = 2;
+		return qual;
+	}
+
+	
+	public ImageSize get_imgSize() throws IOException, ImageOpException{
+		logger.debug("get_imgSize()");
+
+		ImageSize imgSize = get_fileToLoad().getSize();
+		return imgSize;
+	}
+	
+	public Rectangle2D get_userImgArea() throws IOException, ImageOpException{
+		//logger.debug("get_userImgArea()");
+		
+		if(userImgArea==null){
+			// transform from relative [0,1] to image coordinates.
+			AffineTransform imgTrafo = AffineTransform.getScaleInstance(get_imgSize()
+					.getWidth(), get_imgSize().getHeight());
+	
+			// user window area in [0,1] coordinates
+			Rectangle2D relUserArea = new Rectangle2D.Float(getAsFloat("wx"), getAsFloat("wy"),
+					getAsFloat("ww"), getAsFloat("wh"));
+	
+			// transform user coordinate area to image coordinate area
+			userImgArea = imgTrafo.createTransformedShape(
+					relUserArea).getBounds2D();
+			
+			if(get_absoluteScale()){
+				float areaWidth = getAsInt("dw") / get_scaleXY() * getAsFloat("ws");
+				float areaHeight = getAsInt("dh") / get_scaleXY() * getAsFloat("ws");
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+			} else if (!get_scaleToFit()){
+				// crop to fit
+				float areaWidth = getAsInt("dw") * getAsFloat("ws");
+				float areaHeight = getAsInt("dh") * getAsFloat("ws");
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+			}
+		}
+		return userImgArea;		
+		
+	}
+	
+	public Rectangle2D get_outerUserImgArea() throws IOException, ImageOpException{
+		//logger.debug("get_outerUserImgArea()");
+
+		if(outerUserImgArea == null){
+			Rectangle2D userImgArea = get_userImgArea();
+			
+			// image size in pixels
+			Rectangle2D imgBounds = new Rectangle2D.Float(0, 0, get_imgSize()
+					.getWidth(), get_imgSize().getHeight());
+	
+	
+	
+			
+			outerUserImgArea = userImgArea;
+			Rectangle2D innerUserImgArea = userImgArea;
+			if (get_wholeRotArea()) {
+				if (getAsFloat("rot") != 0) {
+					try {
+						// rotate user area coordinates around center of user
+						// area
+						AffineTransform rotTrafo = AffineTransform
+								.getRotateInstance(Math.toRadians(getAsFloat("rot")),
+										userImgArea.getCenterX(), userImgArea
+												.getCenterY());
+						// get bounds from rotated end position
+						innerUserImgArea = rotTrafo.createTransformedShape(
+								userImgArea).getBounds2D();
+						// get bounds from back-rotated bounds
+						outerUserImgArea = rotTrafo.createInverse()
+								.createTransformedShape(innerUserImgArea)
+								.getBounds2D();
+					} catch (NoninvertibleTransformException e1) {
+						// this shouldn't happen anyway
+						logger.error(e1);
+					}
+				}
+			}
+	
+			// logger.debug("Scale " + scaleXY + "(" + scaleX + "," + scaleY
+					//+ ") on " + outerUserImgArea);
+	
+			// clip area at the image border
+			outerUserImgArea = outerUserImgArea.createIntersection(imgBounds);
+	
+			// check image parameters sanity
+			logger.debug("outerUserImgArea.getWidth()=" + outerUserImgArea.getWidth());
+			logger.debug("get_scaleXY() * outerUserImgArea.getWidth() = " + (get_scaleXY() * outerUserImgArea.getWidth()));
+			
+			if ((outerUserImgArea.getWidth() < 1)
+					|| (outerUserImgArea.getHeight() < 1)
+					|| (get_scaleXY() * outerUserImgArea.getWidth() < 2)
+					|| (get_scaleXY() * outerUserImgArea.getHeight() < 2)) {
+				logger.error("ERROR: invalid scale parameter set!");
+				throw new ImageOpException("Invalid scale parameter set!");
+			}
+		}
+		return outerUserImgArea;
+	}
+	
+	
+	public Rectangle2D get_innerUserImgArea() throws IOException, ImageOpException{
+		logger.debug("get_innerUserImgArea()");
+
+		Rectangle2D userImgArea = get_userImgArea();
+		Rectangle2D innerUserImgArea = get_userImgArea();
+		if (get_wholeRotArea()) {
+			if (getAsFloat("rot") != 0) {
+				// rotate user area coordinates around center of user
+				// area
+				AffineTransform rotTrafo = AffineTransform
+						.getRotateInstance(Math.toRadians(getAsFloat("rot")),
+								userImgArea.getCenterX(), userImgArea
+										.getCenterY());
+				// get bounds from rotated end position
+				innerUserImgArea = rotTrafo.createTransformedShape(
+						userImgArea).getBounds2D();
+			}
+		}
+		return innerUserImgArea;
+
+	}
+	public boolean get_wholeRotArea(){
+		// TODO this is not really implemented yet
+		//boolean wholeRotArea = false;
+		return false;//wholeRotArea;
+	}
+	
+	public int get_forceType(){
+
+		if(hasOption("mo","jpg"))
+			return ImageOps.TYPE_JPEG;
+		if(hasOption("mo","png"))
+			return ImageOps.TYPE_PNG;
+		
+		return ImageOps.TYPE_AUTO;
+	}
+	
+	public float[] get_paramRGBM(){
+		logger.debug("get_paramRGBM()");
+
+		float[] paramRGBM = {0f,0f,0f};
+		Parameter p = get("rgbm");
+		if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+			return p.parseAsFloatArray("/");
+		}	
+		return paramRGBM;
+	}
+	
+	public float[] get_paramRGBA(){
+		logger.debug("get_paramRGBA()");
+
+		float[] paramRGBA =  {0f,0f,0f};
+		Parameter p = get("rgba");
+		if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+			paramRGBA = p.parseAsFloatArray("/");
+		}
+		return paramRGBA;
+	}
+	
+	public boolean get_hmir(){
+		logger.debug("get_hmir()");
+
+		return hasOption("mo","hmir");
+	}
+	
+	public boolean get_vmir(){
+		logger.debug("get_vmir()");
+
+		return hasOption("mo","vmir");
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/PDFCache.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,60 @@
+package digilib.servlet;
+
+import java.io.InputStream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class PDFCache extends RequestHandler {
+
+	
+	public static Integer STATUS_DONE = 0;  	// document exists in cache
+
+	public static Integer STATUS_WIP = 1;  		// document is "work in progress"
+	
+	public static Integer STATUS_NONEXISTENT = 2;  	// document does not exist in cache and is not in progress
+	
+	public static Integer STATUS_ERROR = 3;     // an error occurred while processing the request
+
+	
+	
+	
+	@Override
+	public void processRequest(HttpServletRequest request,
+			HttpServletResponse response) {
+
+		// evaluate request ( make a PDFJobDeclaration , get the DocumentId)
+		
+		// check, which state the requested document is in (ready, work in progress, non existent) 
+		
+		// if necessary, initialize document generation (and notify the user)
+		
+		// send the document
+	}
+
+	@Override
+	public void sendFile(InputStream is, HttpServletResponse response, String filename) {
+		
+	}
+
+	public String getDocumentId(PDFJobDeclaration jobdeclaration){
+		// generate an unambiguous ID from the request (this is used for filenames etc)
+		String id;
+
+		String fn = jobdeclaration.getAsString("fn");
+		String dh = jobdeclaration.getAsString("dh");
+		String pgs = jobdeclaration.getAsString("pgs");
+		
+		id = "fn=" + fn + "&dh=" + dh + "&pgs=" + pgs + ".pdf";		
+
+		return id;
+	}
+
+	
+	/** check the status of the document corresponding to the documentid */
+	public Integer getStatus(String documentid){
+		
+		
+		return 0;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/PDFJobDeclaration.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,112 @@
+package digilib.servlet;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+/** 
+ * A container class for storing a set of instructional parameters 
+ * used for content generating classes like MakePDF.  
+ * 
+ * 
+ * @author cmielack
+ *
+ */
+
+
+
+public class PDFJobDeclaration extends ParameterMap {
+
+	String[] parameter_list = {"fn","pgs","dw","dh"};/*,
+			"wx", "wy", "ww", "wh", "ws", 
+			"mo", "rot", "cont", "brgt", "rgbm", "rbgm", 
+			"ddpi", "ddpix", "ddpiy", "scale"};*/
+	
+	public PDFJobDeclaration() {
+		super(30);
+		
+		// url of the page/document (second part)
+		newParameter("fn", "", null, 's');
+		// page number
+		newParameter("pgs", "", null, 's');
+		// width of client in pixels
+		newParameter("dw", new Integer(0), null, 's');
+		// height of client in pixels
+		newParameter("dh", new Integer(0), null, 's');
+		// left edge of image (float from 0 to 1)
+/*		newParameter("wx", new Float(0), null, 's');
+		// top edge in image (float from 0 to 1)
+		newParameter("wy", new Float(0), null, 's');
+		// width of image (float from 0 to 1)
+		newParameter("ww", new Float(1), null, 's');
+		// height of image (float from 0 to 1)
+		newParameter("wh", new Float(1), null, 's');
+		// scale factor
+		newParameter("ws", new Float(1), null, 's');
+		// special options like 'fit' for gifs
+		newParameter("mo", "", null, 's');
+		// rotation angle (degree)
+		newParameter("rot", new Float(0), null, 's');
+		// contrast enhancement factor
+		newParameter("cont", new Float(0), null, 's');
+		// brightness enhancement factor
+		newParameter("brgt", new Float(0), null, 's');
+		// color multiplicative factors
+		newParameter("rgbm", "0/0/0", null, 's');
+		// color additive factors
+		newParameter("rgba", "0/0/0", null, 's');
+		// display dpi resolution (total)
+		newParameter("ddpi", new Float(0), null, 's');
+		// display dpi X resolution
+		newParameter("ddpix", new Float(0), null, 's');
+		// display dpi Y resolution
+		newParameter("ddpiy", new Float(0), null, 's');
+		// scale factor for mo=ascale
+		newParameter("scale", new Float(1), null, 's');
+*/
+		/*
+		 * Parameters of type 'i' are not exchanged between client and server,
+		 * but are for the servlets or JSPs internal use.
+		 */
+
+/*		// url of the page/document (first part, may be empty)
+		newParameter("request.path", "", null, 'i');
+		// base URL (from http:// to below /servlet)
+		newParameter("base.url", null, null, 'i');
+		// DocuImage instance for this request
+*/
+		/*
+		 * Parameters of type 'c' are for the clients use
+		 */
+
+/*		// "real" filename
+		newParameter("img.fn", "", null, 'c');
+		// image dpi x
+		newParameter("img.dpix", new Integer(0), null, 'c');
+		// image dpi y
+		newParameter("img.dpiy", new Integer(0), null, 'c');
+		// hires image size x
+		newParameter("img.pix_x", new Integer(0), null, 'c');
+		// hires image size y
+		newParameter("img.pix_y", new Integer(0), null, 'c');
+		// total number of pages
+		newParameter("pt", new Integer(0), null, 'c');
+		// display level of digilib (0 = just image, 1 = one HTML page
+		// 2 = in frameset, 3 = XUL-'frameset'
+		// 4 = XUL-Sidebar )
+		newParameter("lv", new Integer(2), null, 'c');
+		// marks
+		newParameter("mk", "", null, 'c');
+*/	
+		
+	}
+
+	public void setWithRequest(HttpServletRequest request) {
+		for (String param : parameter_list){
+			if (request.getParameterMap().containsKey(param)){
+				put(param, request.getAttribute(param));
+			}
+		}
+	}
+	
+	
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/PDFMaker.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,931 @@
+/*
+ * MakePDF
+ * 
+ * Digital Image Library servlet components
+ * 
+ * Copyright (C) 200-2004 Robert Casties (robcast@mail.berlios.de)
+ * 
+ * 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 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * Please read license.txt for the full details. A copy of the GPL may be found
+ * at http://www.gnu.org/copyleft/lgpl.html
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ *  
+ */
+
+package digilib.servlet;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.PdfWriter;
+
+import digilib.auth.AuthOpException;
+import digilib.auth.AuthOps;
+import digilib.image.ImageOpException;
+import digilib.image.ImageOps;
+import digilib.image.ImageSize;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirectory;
+import digilib.io.DocuDirent;
+import digilib.io.FileOpException;
+import digilib.io.FileOps;
+import digilib.io.ImageFile;
+import digilib.io.ImageFileset;
+
+/**
+ * generates pdf-files from a digilib images.
+ * This is a very quick and dirty but functional version and will 
+ * (hopefully soon) be replaced by a parallelized and more sophisticated one.
+ *	
+ * @author cmielack
+ */
+
+public class PDFMaker extends HttpServlet implements Runnable{
+
+	private static final long serialVersionUID = -325080527268912852L;
+
+	/** digilib servlet version (for all components) */
+	public static final String dlVersion = "1.7.0b";
+
+	/** logger for accounting requests */
+	private static Logger accountlog = Logger.getLogger("account.request");
+
+	/** gengeral logger for this class */
+	private static Logger logger = Logger.getLogger("digilib.servlet");
+
+	/** logger for authentication related */
+	private static Logger authlog = Logger.getLogger("digilib.auth");
+
+	/** general error code */
+	public static final int ERROR_UNKNOWN = 0;
+
+	/** error code for authentication error */
+	public static final int ERROR_AUTH = 1;
+
+	/** error code for file operation error */
+	public static final int ERROR_FILE = 2;
+
+	/** error code for image operation error */
+	public static final int ERROR_IMAGE = 3;
+
+	/** DocuDirCache instance */
+	DocuDirCache dirCache;
+
+	/** authentication error image file */
+	File denyImgFile;
+
+	/** image error image file */
+	File errorImgFile;
+
+	/** not found error image file */
+	File notfoundImgFile;
+
+	/** subsampling before scaling */
+	float minSubsample = 2f;
+
+	/** send files as is? */
+	boolean sendFileAllowed = true;
+
+	/** default scaling quality */
+	int defaultQuality = 1;
+
+	/** DigilibConfiguration instance */
+	DigilibConfiguration dlConfig;
+
+	/** use authorization database */
+	boolean useAuthorization = true;
+
+	/** AuthOps instance */
+	AuthOps authOp;
+
+	HttpServletRequest mpdf_request = null;
+	HttpServletResponse mpdf_response = null;
+	String mpdf_filename = "";
+	
+	String default_filename = "digilib_pages.pdf";
+	
+	
+	// EXPRIMENTAL
+	/** try to enlarge cropping area for "oblique" angles */
+	boolean wholeRotArea = false;
+
+	/**
+	 * Initialisation on first run.
+	 * 
+	 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+	 */
+	public void init(ServletConfig config) throws ServletException {
+		super.init(config);
+
+		System.out
+				.println("***** Digital Image Library Image MakePDF Servlet (version "
+						+ dlVersion + ") *****");
+		// say hello in the log file
+		logger
+				.info("***** Digital Image Library Image MakePDF Servlet (version "
+						+ dlVersion + ") *****");
+
+		// get our ServletContext
+		ServletContext context = config.getServletContext();
+		// see if there is a Configuration instance
+		dlConfig = (DigilibConfiguration) context
+				.getAttribute("digilib.servlet.configuration");
+		if (dlConfig == null) {
+			// no Configuration
+			throw new ServletException("No Configuration!");
+		}
+		// set our AuthOps
+		useAuthorization = dlConfig.getAsBoolean("use-authorization");
+		authOp = (AuthOps) dlConfig.getValue("servlet.auth.op");
+
+		// DocuDirCache instance
+		dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+		denyImgFile = ServletOps.getFile((File) dlConfig.getValue("denied-image"), config);
+		errorImgFile = ServletOps.getFile((File) dlConfig.getValue("error-image"), config);
+		notfoundImgFile = ServletOps.getFile((File) dlConfig.getValue("notfound-image"), config);
+		sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed");
+		minSubsample = dlConfig.getAsFloat("subsample-minimum");
+		defaultQuality = dlConfig.getAsInt("default-quality");
+
+	
+		context.setAttribute("digilib.servlet.MakePDF", this); // register this instance globally, so PDFCache can access it
+	}
+
+	
+	public void doCreate(HttpServletRequest request, HttpServletResponse response, String filename)
+		throws ServletException, IOException {
+
+		accountlog.info("GET from " + request.getRemoteAddr());
+		// create new request with defaults
+		DigilibRequest dlReq = new DigilibRequest();
+		// set with request parameters
+		dlReq.setWithRequest(request);
+		// add DigilibRequest to ServletRequest
+		request.setAttribute("digilib.servlet.request", dlReq);
+
+		
+		mpdf_request = request;
+		mpdf_response = response;
+		mpdf_filename = filename;
+		
+	}
+	
+/*	public void doGet(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		
+		accountlog.info("GET from " + request.getRemoteAddr());
+		// create new request with defaults
+		DigilibRequest dlReq = new DigilibRequest();
+		// set with request parameters
+		dlReq.setWithRequest(request);
+		// add DigilibRequest to ServletRequest
+		request.setAttribute("digilib.servlet.request", dlReq);
+		// do the processing
+		processRequest(request, response,default_filename);
+	}*/
+
+/*	public void doPost(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		
+		accountlog.info("POST from " + request.getRemoteAddr());
+		// create new request with defaults
+		DigilibRequest dlReq = new DigilibRequest();
+		// set with request parameters
+		dlReq.setWithRequest(request);
+		// add DigilibRequest to ServletRequest
+		request.setAttribute("digilib.servlet.request", dlReq);
+		// do the processing
+		processRequest(request, response,default_filename);
+	}*/
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see javax.servlet.http.HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest)
+	 */
+
+	protected long getLastModified(HttpServletRequest request) {
+		accountlog.debug("GetLastModified from " + request.getRemoteAddr()
+				+ " for " + request.getQueryString());
+		long mtime = -1;
+		// create new request with defaults
+		DigilibRequest dlReq = new DigilibRequest();
+		// set with request parameters
+		dlReq.setWithRequest(request);
+		// find the requested file
+		DocuDirent f = findFile(dlReq);
+		if (f != null) {
+			DocuDirectory dd = (DocuDirectory) f.getParent();
+			mtime = dd.getDirMTime() / 1000 * 1000;
+		}
+		return mtime;
+	}
+
+	void createPDFfile(HttpServletRequest request, HttpServletResponse response, String filename)
+		throws javax.servlet.ServletException, java.io.IOException {
+		
+		Hashtable<String,Integer> cache_hash = (Hashtable<String,Integer>) this.getServletContext().getAttribute("digilib.servlet.PDFCache");
+		BufferedOutputStream pdfOutStream = null;
+		FileOutputStream fos = null;
+
+		String file_only = filename.split("/")[filename.split("/").length-1]; 
+		
+		try {
+
+			cache_hash.put(file_only, 2); // register the file as 'pending'
+	
+			// TODO check, if file can be created without overwriting another file etc.
+
+			fos = new FileOutputStream(filename);
+			
+			pdfOutStream = generatePDFcontent(request, response, this.getServletContext(), fos);
+			
+			fos.flush();
+
+			cache_hash.put(file_only, 1); // register the file as 'done'
+
+		}
+		catch (DocumentException de){
+			// inform the user about what went wrong
+			response.setContentType("text/html");
+			PrintWriter writer = response.getWriter();
+			writer.println(this.getClass().getName() + " caught an exception: "  
+						   + de.getClass().getName() + "<br/> <pre>");
+			de.printStackTrace(writer);
+			writer.println("</pre>");
+		}
+		
+		finally {
+			if (pdfOutStream != null){
+				pdfOutStream.close();
+			}
+			if (fos!=null){
+				fos.close();
+			}
+		}
+	}
+	
+		
+	
+	
+	void addImageToPDF(HttpServletRequest request, HttpServletResponse response, int pn, Document doc)
+			throws ServletException {
+		/** until now, this is taken from the Scaler-method processRequest and modified ...*/
+		
+		
+		if (dlConfig == null) {
+			throw new ServletException("ERROR: No Configuration!");
+		}
+
+		accountlog.debug("request: " + request.getQueryString());
+		logger.debug("request: " + request.getQueryString());
+
+		// output mime-type
+		String mimeType = "image/png";
+
+		/* preset request parameters */
+
+		// scale the image file to fit window size i.e. respect dw,dh
+		boolean scaleToFit = true;
+		// scale the image by a fixed factor only
+		boolean absoluteScale = false;
+		// use low resolution images only
+		boolean loresOnly = false;
+		// use hires images only
+		boolean hiresOnly = false;
+		// send the image always as a specific type (e.g. JPEG or PNG)
+		int forceType = ImageOps.TYPE_AUTO;
+		// interpolation to use for scaling
+		int scaleQual = defaultQuality;
+		// send html error message (or image file)
+		boolean errorMsgHtml = false;
+		// original (hires) image resolution
+		float origResX = 0;
+		float origResY = 0;
+
+		/* request parameters */
+
+		DigilibRequest dlRequest = (DigilibRequest) request
+				.getAttribute("digilib.servlet.request");
+
+		dlRequest.setValue("pn", pn);
+		//dlRequest.setValue("mo", "osize");
+		//dlRequest.setValue("ddpix",72);
+		//dlRequest.setValue("ddpix",72);
+		
+		// destination image width
+		int paramDW = dlRequest.getAsInt("dw");
+		// destination image height
+		int paramDH = dlRequest.getAsInt("dh");
+		// relative area x_offset (0..1)
+		float paramWX = dlRequest.getAsFloat("wx");
+		// relative area y_offset
+		float paramWY = dlRequest.getAsFloat("wy");
+		// relative area width (0..1)
+		float paramWW = dlRequest.getAsFloat("ww");
+		// relative area height
+		float paramWH = dlRequest.getAsFloat("wh");
+		// scale factor (additional to dw/width, dh/height)
+		float paramWS = dlRequest.getAsFloat("ws");
+		// rotation angle
+		float paramROT = dlRequest.getAsFloat("rot");
+		// contrast enhancement
+		float paramCONT = dlRequest.getAsFloat("cont");
+		// brightness enhancement
+		float paramBRGT = dlRequest.getAsFloat("brgt");
+		// color modification
+		float[] paramRGBM = null;
+		Parameter p = dlRequest.get("rgbm");
+		if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+			paramRGBM = p.parseAsFloatArray("/");
+		}
+		float[] paramRGBA = null;
+		p = dlRequest.get("rgba");
+		if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+			paramRGBA = p.parseAsFloatArray("/");
+		}
+		// destination resolution (DPI)
+		float paramDDPIX = dlRequest.getAsFloat("ddpix");
+		float paramDDPIY = dlRequest.getAsFloat("ddpiy");
+		if ((paramDDPIX == 0) || (paramDDPIY == 0)) {
+			// if X or Y resolution isn't set, use DDPI
+			paramDDPIX = dlRequest.getAsFloat("ddpi");
+			paramDDPIY = paramDDPIX;
+		}
+		// absolute scale factor for mo=ascale (and mo=osize)
+		float paramSCALE = dlRequest.getAsFloat("scale");
+
+		/*
+		 * operation mode: "fit": always fit to page, "clip": send original
+		 * resolution cropped, "file": send whole file (if allowed)
+		 */
+		if (dlRequest.hasOption("mo", "clip")) {
+			scaleToFit = false;
+			absoluteScale = false;
+			hiresOnly = true;
+		} else if (dlRequest.hasOption("mo", "fit")) {
+			scaleToFit = true;
+			absoluteScale = false;
+			hiresOnly = false;
+		} else if (dlRequest.hasOption("mo", "osize")) {
+			scaleToFit = false;
+			absoluteScale = true;
+			hiresOnly = true;
+		} else if (dlRequest.hasOption("mo", "ascale")) {
+			scaleToFit = false;
+			absoluteScale = true;
+			hiresOnly = false;
+		}
+
+		// operation mode: "lores": try to use scaled image, "hires": use
+		// unscaled image
+		// "autores": try best fitting resolution
+		if (dlRequest.hasOption("mo", "lores")) {
+			loresOnly = true;
+			hiresOnly = false;
+		} else if (dlRequest.hasOption("mo", "hires")) {
+			loresOnly = false;
+			hiresOnly = true;
+		} else if (dlRequest.hasOption("mo", "autores")) {
+			loresOnly = false;
+			hiresOnly = false;
+		}
+		// operation mode: "errtxt": error message in html, "errimg": error
+		// image
+		if (dlRequest.hasOption("mo", "errtxt")) {
+			errorMsgHtml = true;
+		} else if (dlRequest.hasOption("mo", "errimg")) {
+			errorMsgHtml = false;
+		}
+		// operation mode: "q0" - "q2": interpolation quality
+		if (dlRequest.hasOption("mo", "q0")) {
+			scaleQual = 0;
+		} else if (dlRequest.hasOption("mo", "q1")) {
+			scaleQual = 1;
+		} else if (dlRequest.hasOption("mo", "q2")) {
+			scaleQual = 2;
+		}
+		// operation mode: "jpg": always use JPEG
+		if (dlRequest.hasOption("mo", "jpg")) {
+			forceType = ImageOps.TYPE_JPEG;
+		}
+		// operation mode: "png": always use PNG
+		if (dlRequest.hasOption("mo", "png")) {
+			forceType = ImageOps.TYPE_PNG;
+		}
+
+		// check with the maximum allowed size (if set)
+		int maxImgSize = dlConfig.getAsInt("max-image-size");
+		if (maxImgSize > 0) {
+			paramDW = (paramDW * paramWS > maxImgSize) ? (int) (maxImgSize / paramWS)
+					: paramDW;
+			paramDH = (paramDH * paramWS > maxImgSize) ? (int) (maxImgSize / paramWS)
+					: paramDH;
+		}
+
+		// "big" try for all file/image actions
+		try {
+
+			// ImageFileset of the image to load
+			ImageFileset fileset = null;
+
+			/* find the file to load/send */
+
+			// get PathInfo
+			String loadPathName = dlRequest.getFilePath();
+
+			/* check permissions */
+			if (useAuthorization) {
+				// get a list of required roles (empty if no restrictions)
+				List rolesRequired = authOp.rolesForPath(loadPathName, request);
+				if (rolesRequired != null) {
+					authlog.debug("Role required: " + rolesRequired);
+					authlog.debug("User: " + request.getRemoteUser());
+					// is the current request/user authorized?
+					if (!authOp.isRoleAuthorized(rolesRequired, request)) {
+						// send deny answer and abort
+						throw new AuthOpException();
+					}
+				}
+			}
+
+			// find the file
+			fileset = (ImageFileset) findFile(dlRequest);
+			if (fileset == null) {
+				throw new FileOpException("File " + loadPathName + "("
+						+ dlRequest.getAsInt("pn") + ") not found.");
+			}
+
+			/* for absolute scale and original size we need the hires size */
+			ImageSize hiresSize = null;
+			if (absoluteScale) {
+				ImageFile hiresFile = fileset.getBiggest();
+				if (!hiresFile.isChecked()) {
+					ImageOps.checkFile(hiresFile);
+				}
+				hiresSize = hiresFile.getSize();
+				
+				/* prepare resolution and scale factor for original size */
+				if (dlRequest.hasOption("mo", "osize")) {
+					// get original resolution from metadata
+					fileset.checkMeta();
+					origResX = fileset.getResX();
+					origResY = fileset.getResY();
+					if ((origResX == 0) || (origResY == 0)) {
+						throw new ImageOpException("Missing image DPI information!");
+					}
+
+					if ((paramDDPIX == 0) || (paramDDPIY == 0)) {
+						throw new ImageOpException(
+								"Missing display DPI information!");
+					}
+					// calculate absolute scale factor
+					float sx = paramDDPIX / origResX;
+					float sy = paramDDPIY / origResY;
+					// currently only same scale :-(
+					paramSCALE = (sx + sy)/2f;
+				}
+				
+			}
+			
+
+			/* calculate expected source image size */
+			ImageSize expectedSourceSize = new ImageSize();
+			if (scaleToFit) {
+				// scale to fit -- calculate minimum source size
+				float scale = (1 / Math.min(paramWW, paramWH)) * paramWS;
+				expectedSourceSize.setSize((int) (paramDW * scale),
+						(int) (paramDH * scale));
+			} else if (absoluteScale && dlRequest.hasOption("mo", "ascale")) {
+				// absolute scale -- apply scale to hires size
+				expectedSourceSize = hiresSize.getScaled(paramSCALE);
+			} else {
+				// clip to fit -- source = destination size
+				expectedSourceSize.setSize((int) (paramDW * paramWS),
+						(int) (paramDH * paramWS));
+			}
+
+			ImageFile fileToLoad;
+			/* select a resolution */
+			if (hiresOnly) {
+				// get first element (= highest resolution)
+				fileToLoad = fileset.getBiggest();
+			} else if (loresOnly) {
+				// enforced lores uses next smaller resolution
+				fileToLoad = fileset.getNextSmaller(expectedSourceSize);
+				if (fileToLoad == null) {
+					// this is the smallest we have
+					fileToLoad = fileset.getSmallest();
+				}
+			} else {
+				// autores: use next higher resolution
+				fileToLoad = fileset.getNextBigger(expectedSourceSize);
+				if (fileToLoad == null) {
+					// this is the highest we have
+					fileToLoad = fileset.getBiggest();
+				}
+			}
+			logger.info("Planning to load: " + fileToLoad.getFile());
+
+			/*
+			 * send the image if its mo=(raw)file
+			 */
+			if (dlRequest.hasOption("mo", "file")
+					|| dlRequest.hasOption("mo", "rawfile")) {
+				if (sendFileAllowed) {
+					String mt = null;
+					if (dlRequest.hasOption("mo", "rawfile")) {
+						mt = "application/octet-stream";
+					}
+					logger.debug("Sending RAW File as is.");
+					ServletOps.sendFile(fileToLoad.getFile(), mt, response);
+					return;
+				}
+			}
+
+			// check the source image
+			if (!fileToLoad.isChecked()) {
+				ImageOps.checkFile(fileToLoad);
+			}
+			// get the source image type
+			mimeType = fileToLoad.getMimetype();
+			// get the source image size
+			ImageSize imgSize = fileToLoad.getSize();
+
+			// decide if the image can be sent as is
+			boolean mimetypeSendable = mimeType.equals("image/jpeg")
+					|| mimeType.equals("image/png")
+					|| mimeType.equals("image/gif");
+			boolean imagoOptions = dlRequest.hasOption("mo", "hmir")
+					|| dlRequest.hasOption("mo", "vmir") || (paramROT != 0)
+					|| (paramRGBM != null) || (paramRGBA != null)
+					|| (paramCONT != 0) || (paramBRGT != 0);
+			boolean imageSendable = mimetypeSendable && !imagoOptions;
+
+			/*
+			 * if not autoRes and image smaller than requested size then send as
+			 * is. if autoRes and image has requested size then send as is. if
+			 * not autoScale and not scaleToFit nor cropToFit then send as is
+			 * (mo=file)
+			 */
+/*			if (imageSendable
+					&& ((loresOnly && fileToLoad.getSize().isSmallerThan(
+							expectedSourceSize)) || (!(loresOnly || hiresOnly) && fileToLoad
+							.getSize().fitsIn(expectedSourceSize)))) {
+
+				logger.debug("Sending File as is.");
+
+				ServletOps.sendFile(fileToLoad.getFile(), null, response);
+
+				logger.info("Done in "
+						+ (System.currentTimeMillis() - startTime) + "ms");
+				return;
+			}
+*/
+ 
+			
+			/*
+			 * stop here if we're overloaded...
+			 * 
+			 * 503 Service Unavailable 
+			 * The server is currently unable to
+			 * handle the request due to a temporary overloading or maintenance
+			 * of the server. The implication is that this is a temporary
+			 * condition which will be alleviated after some delay. If known,
+			 * the length of the delay MAY be indicated in a Retry-After header.
+			 * If no Retry-After is given, the client SHOULD handle the response
+			 * as it would for a 500 response. Note: The existence of the 503
+			 * status code does not imply that a server must use it when
+			 * becoming overloaded. Some servers may wish to simply refuse the
+			 * connection.
+			 * (RFC2616 HTTP1.1)
+			 */
+			if (! DigilibWorker.canRun()) {
+				logger.error("Servlet overloaded!");
+				response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+				return;
+			}
+			
+			// set missing dw or dh from aspect ratio
+			float imgAspect = fileToLoad.getAspect();
+			if (paramDW == 0) {
+				paramDW = (int) Math.round(paramDH * imgAspect);
+			} else if (paramDH == 0) {
+				paramDH = (int) Math.round(paramDW / imgAspect);
+			}
+
+			/* crop and scale the image */
+
+			logger.debug("IMG: " + imgSize.getWidth() + "x"
+					+ imgSize.getHeight());
+
+			// coordinates and scaling
+			float areaWidth;
+			float areaHeight;
+			float scaleX;
+			float scaleY;
+			float scaleXY;
+
+			// coordinates using Java2D
+			// image size in pixels
+			Rectangle2D imgBounds = new Rectangle2D.Float(0, 0, imgSize
+					.getWidth(), imgSize.getHeight());
+			// user window area in [0,1] coordinates
+			Rectangle2D relUserArea = new Rectangle2D.Float(paramWX, paramWY,
+					paramWW, paramWH);
+			// transform from relative [0,1] to image coordinates.
+			AffineTransform imgTrafo = AffineTransform.getScaleInstance(imgSize
+					.getWidth(), imgSize.getHeight());
+			// transform user coordinate area to image coordinate area
+			Rectangle2D userImgArea = imgTrafo.createTransformedShape(
+					relUserArea).getBounds2D();
+
+			// calculate scaling factors based on inner user area
+			if (scaleToFit) {
+				areaWidth = (float) userImgArea.getWidth();
+				areaHeight = (float) userImgArea.getHeight();
+				scaleX = paramDW / areaWidth * paramWS;
+				scaleY = paramDH / areaHeight * paramWS;
+				scaleXY = (scaleX > scaleY) ? scaleY : scaleX;
+			} else if (absoluteScale) {
+				scaleXY = paramSCALE;
+				// we need to correct the factor if we use a pre-scaled image
+				if (imgSize.getWidth() != hiresSize.getWidth()) {
+					scaleXY *= (float)hiresSize.getWidth() / (float)imgSize.getWidth();
+				}
+				scaleX = scaleXY;
+				scaleY = scaleXY;
+				areaWidth = paramDW / scaleXY * paramWS;
+				areaHeight = paramDH / scaleXY * paramWS;
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+			} else {
+				// crop to fit
+				areaWidth = paramDW * paramWS;
+				areaHeight = paramDH * paramWS;
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+				scaleX = 1f;
+				scaleY = 1f;
+				scaleXY = 1f;
+			}
+
+			// enlarge image area for rotations to cover additional pixels
+			Rectangle2D outerUserImgArea = userImgArea;
+			Rectangle2D innerUserImgArea = userImgArea;
+			if (wholeRotArea) {
+				if (paramROT != 0) {
+					try {
+						// rotate user area coordinates around center of user
+						// area
+						AffineTransform rotTrafo = AffineTransform
+								.getRotateInstance(Math.toRadians(paramROT),
+										userImgArea.getCenterX(), userImgArea
+												.getCenterY());
+						// get bounds from rotated end position
+						innerUserImgArea = rotTrafo.createTransformedShape(
+								userImgArea).getBounds2D();
+						// get bounds from back-rotated bounds
+						outerUserImgArea = rotTrafo.createInverse()
+								.createTransformedShape(innerUserImgArea)
+								.getBounds2D();
+					} catch (NoninvertibleTransformException e1) {
+						// this shouldn't happen anyway
+						logger.error(e1);
+					}
+				}
+			}
+
+			logger.debug("Scale " + scaleXY + "(" + scaleX + "," + scaleY
+					+ ") on " + outerUserImgArea);
+
+			// clip area at the image border
+			outerUserImgArea = outerUserImgArea.createIntersection(imgBounds);
+
+			// check image parameters sanity
+			if ((outerUserImgArea.getWidth() < 1)
+					|| (outerUserImgArea.getHeight() < 1)
+					|| (scaleXY * outerUserImgArea.getWidth() < 2)
+					|| (scaleXY * outerUserImgArea.getHeight() < 2)) {
+				logger.error("ERROR: invalid scale parameter set!");
+				throw new ImageOpException("Invalid scale parameter set!");
+			}
+
+			/*
+			 * submit the image worker job
+			 */
+
+			DigilibPDFWorker job = new DigilibPDFWorker(dlConfig, response,
+					mimeType, scaleQual, dlRequest, paramCONT,
+					paramBRGT, paramRGBM, paramRGBA, fileToLoad, scaleXY,
+					outerUserImgArea, innerUserImgArea, minSubsample,
+					wholeRotArea, forceType, doc);
+
+			job.run();
+			if (job.hasError()) {
+				throw new ImageOpException(job.getError().toString());
+			}
+
+			/* error handling */
+
+		} // end of "big" try
+		catch (IOException e) {
+			logger.error("ERROR: File IO Error: " + e);
+			digilibError(errorMsgHtml, ERROR_FILE,
+					"ERROR: File IO Error: " + e, response);
+		} catch (AuthOpException e) {
+			logger.error("ERROR: Authorization error: " + e);
+			digilibError(errorMsgHtml, ERROR_AUTH,
+					"ERROR: Authorization error: " + e, response);
+		} catch (ImageOpException e) {
+			logger.error("ERROR: Image Error: " + e);
+			digilibError(errorMsgHtml, ERROR_IMAGE,
+					"ERROR: Image Operation Error: " + e, response);
+		} catch (RuntimeException e) {
+			// JAI likes to throw RuntimeExceptions ;-(
+			logger.error("ERROR: Other Image Error: " + e);
+			digilibError(errorMsgHtml, ERROR_IMAGE,
+					"ERROR: Other Image Operation Error: " + e, response);
+		}
+	}
+
+	/**
+	 * Returns the DocuDirent corresponding to the DigilibRequest.
+	 * 
+	 * @param dlRequest
+	 * @return
+	 */
+	public DocuDirent findFile(DigilibRequest dlRequest) {
+		// find the file(set)
+		DocuDirent f = dirCache.getFile(dlRequest.getFilePath(), dlRequest
+				.getAsInt("pn"), FileOps.CLASS_IMAGE);
+		return f;
+	}
+
+	/**
+	 * Sends an error to the client as text or image.
+	 * 
+	 * @param asHTML
+	 * @param type
+	 * @param msg
+	 * @param response
+	 */
+	public void digilibError(boolean asHTML, int type, String msg,
+			HttpServletResponse response) {
+		try {
+			File img = null;
+			if (type == ERROR_AUTH) {
+				if (msg == null) {
+					msg = "ERROR: Unauthorized access!";
+				}
+				img = denyImgFile;
+			} else if (type == ERROR_FILE) {
+				if (msg == null) {
+					msg = "ERROR: Image file not found!";
+				}
+				img = notfoundImgFile;
+			} else {
+				if (msg == null) {
+					msg = "ERROR: Other image error!";
+				}
+				img = this.errorImgFile;
+			}
+			if (asHTML && (img != null)) {
+				ServletOps.htmlMessage(msg, response);
+			} else {
+				ServletOps.sendFile(img, null, response);
+			}
+		} catch (IOException e) {
+			logger.error("Error sending error!", e);
+		}
+
+	}
+
+	/**
+	 * @return the dlVersion
+	 */
+	public static String getVersion() {
+		return dlVersion;
+	}
+
+	
+	protected BufferedOutputStream generatePDFcontent(HttpServletRequest request, HttpServletResponse response, 
+																	ServletContext servletcontext, OutputStream out)
+	 	throws DocumentException {
+		
+		/** This method sets up a document and calls the addImage method to add all the images */
+		Document doc = new Document(PageSize.A4,0,0,0,0);
+		BufferedOutputStream outstream = new BufferedOutputStream(out);
+
+		PdfWriter docwriter = null;
+
+
+		try{
+
+			docwriter = PdfWriter.getInstance(doc, outstream);
+
+	
+			doc.addAuthor(this.getClass().getName());
+			doc.addCreationDate();
+			doc.addKeywords("digilib");
+			doc.addTitle("digilib PDF");
+			doc.addCreator(this.getClass().getName());
+			
+			doc.open();
+		
+			
+			// get width and height from the request
+/*			float docW = PageSize.A4.getWidth() - 2*PageSize.A4.getBorder(); 
+			float docH= PageSize.A4.getHeight()- 2*PageSize.A4.getBorder();
+*/
+			
+			// evaluate the pgs parameter (which pages go into the pdf)
+			String pages    = request.getParameter("pgs");
+			ArrayList<Integer> pgs=new ArrayList<Integer>(); // a list of the requested page numbers
+			String intervals[] = pages.split(",");
+		
+			// convert the page-interval-strings into a list containing every single page
+			for(String interval: intervals){
+				if(interval.indexOf("-") > -1){
+					String nums[] = interval.split("-");
+					
+					for(int i=Integer.valueOf(nums[0]); i <= Integer.valueOf(nums[1]); i++){
+						pgs.add(i);
+					}
+				}
+				else{
+					pgs.add(Integer.valueOf(interval));
+				}
+			}
+
+	
+
+		
+			// add all the images/pages to the pdf
+			for(int i=0; i<pgs.size(); i++){
+				int pn=pgs.get(i);
+				this.addImageToPDF(request, response, pn, doc);
+			}
+		
+	
+	
+		}
+		catch (Exception de) {
+			logger.debug(de.getMessage());
+		} 
+		finally{
+			if (doc!=null){
+				doc.close();
+			}
+			if (docwriter!=null){
+				docwriter.close();
+			}
+		}
+		return outstream;
+	}
+
+
+	public void run() {
+		if(mpdf_request!=null && mpdf_response!=null && mpdf_filename!=""){
+			try {
+				createPDFfile(mpdf_request, mpdf_response, mpdf_filename);
+			} catch (ServletException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/RequestHandler.java	Mon Feb 23 14:45:22 2009 +0100
@@ -0,0 +1,83 @@
+package digilib.servlet;
+
+import java.io.InputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+
+import digilib.image.ImageOpException;
+
+public abstract class RequestHandler extends HttpServlet {
+
+	/** logger for accounting requests */
+	protected static Logger accountlog = Logger.getLogger("account.request");
+
+	/** gengeral logger for this class */
+	protected static Logger logger = Logger.getLogger("digilib.servlet");
+
+	/** logger for authentication related */
+	protected static Logger authlog = Logger.getLogger("digilib.auth");
+
+	
+	public void init(){
+		try {
+			super.init();
+		} catch (ServletException e) {
+			e.printStackTrace();
+			logger.error(e.getMessage());
+		}
+
+		
+		
+	}
+	
+	
+	public void doGet(HttpServletRequest request, HttpServletResponse response){
+		accountlog.info("GET from " + request.getRemoteAddr());
+		
+		try {
+			this.processRequest(request, response);
+		} catch (ServletException e) {
+			e.printStackTrace();
+			logger.error(e.getMessage());
+		} catch (ImageOpException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+
+	public void doPost(HttpServletRequest request, HttpServletResponse response){
+		accountlog.info("POST from " + request.getRemoteAddr());
+
+		try {
+			this.processRequest(request, response);
+		} catch (ServletException e) {
+			e.printStackTrace();
+			logger.error(e.getMessage());
+		} catch (ImageOpException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * processRequest 
+	 * 
+	 * evaluate request (,generate content), send content to user
+	 * @throws ServletException 
+	 * @throws ImageOpException 
+	 * 
+	 * */
+	abstract public void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, ImageOpException;
+	
+	
+	/** send the requested content to the response */
+	// abstract public void sendFile(File f, HttpServletResponse response, String filename);
+	
+	
+}
--- a/servlet/src/digilib/servlet/Scaler.java	Thu Jan 17 15:25:46 2002 +0100
+++ b/servlet/src/digilib/servlet/Scaler.java	Mon Feb 23 14:45:22 2009 +0100
@@ -1,377 +1,295 @@
-/* Scaler -- Scaler servlet main class
-
-  Digital Image Library servlet components
-
-  Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de)
-
-  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 2 of the  License, or (at your
-  option) any later version.
-   
-  Please read license.txt for the full details. A copy of the GPL
-  may be found at http://www.gnu.org/copyleft/lgpl.html
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-*/
-
 package digilib.servlet;
 
-import javax.servlet.*;
-import javax.servlet.http.*;
-import java.io.*;
-import java.util.*;
+import java.awt.Image;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
-import digilib.*;
-import digilib.io.*;
-import digilib.image.*;
-import digilib.auth.*;
+import org.apache.log4j.Logger;
 
+import digilib.auth.AuthOpException;
+import digilib.auth.AuthOps;
+import digilib.image.ImageOpException;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirent;
+import digilib.io.FileOps;
+import digilib.io.ImageFile;
+import digilib.io.ImageFileset;
 
-//public class Scaler extends HttpServlet implements SingleThreadModel {
-public class Scaler extends HttpServlet {
+public class Scaler extends RequestHandler {
+
+	/** digilib servlet version (for all components) */
+	public static final String dlVersion = "1.7.0b";
 
-  // Utils instance with debuglevel
-  Utils util;
-  // ServletOpss instance
-  ServletOps servletOp;
-  // FileOps instance
-  FileOps fileOp;
-  // AuthOps instance
-  AuthOps authOp;
-  // global DocuImage instance (don't reuse inside a request!)
-  DocuImage globalImage;
+	/** general error code */
+	public static final int ERROR_UNKNOWN = 0;
+
+	/** error code for authentication error */
+	public static final int ERROR_AUTH = 1;
+
+	/** error code for file operation error */
+	public static final int ERROR_FILE = 2;
+
+	/** error code for image operation error */
+	public static final int ERROR_IMAGE = 3;
+
+	/** DocuDirCache instance */
+	DocuDirCache dirCache;
 
-  // use authorization database
-  boolean useAuthentication = true;
-  // image file to send in case of error
-  File errorImgFile = new File("/docuserver/images/icons/scalerror.gif");
-  // image file to send if access is denied
-  File denyImgFile = new File("/docuserver/images/icons/denied.gif");
-  // base directories in order of preference (prescaled versions first)
-  String[] baseDirs = {"/docuserver/scaled/small", "/docuserver/images", "/docuserver/scans/quellen"};
+	/** authentication error image file */
+	File denyImgFile;
 
+	/** image error image file */
+	File errorImgFile;
+
+	/** not found error image file */
+	File notfoundImgFile;
 
-  /*********************************************************
-   *             Initialize global variables
-   *********************************************************/
-  public void init(ServletConfig config) throws ServletException {
-    super.init(config);
+	/** subsampling before scaling */
+	float minSubsample = 2f;
 
-    // first we need an Utils to setup ServletOps UGLY!!
-    util = new Utils(5);
-    // servletOps takes a ServletConfig to get the config file name
-    servletOp = new ServletOps(util, config);
-    // then we can start reading parameters UGLY!!
+	/** send files as is? */
+	boolean sendFileAllowed = true;
+
+	/** default scaling quality */
+	int defaultQuality = 1;
 
-    // Utils instance with debuglevel
-    int debugLevel = servletOp.tryToGetInitParam("debug-level", 10);
-    util = new Utils(debugLevel);
-    // reset Util for ServletOps instance
-    servletOp.setUtils(util);
-    // image file to send in case of error
-    String errorImgFileName = servletOp.tryToGetInitParam("error-image", "/docuserver/images/icons/scalerror.gif");
-    errorImgFile = new File(errorImgFileName);
-    // image file to send if access is denied
-    String denyImgFileName = servletOp.tryToGetInitParam("denied-image", "/docuserver/images/icons/denied.gif");
-    denyImgFile = new File(denyImgFileName);
-    // base directories in order of preference (prescaled versions first)
-    String baseDirList = servletOp.tryToGetInitParam("basedir-list", "/docuserver/scaled/small:/docuserver/images:/docuserver/scans/quellen");
-    // split list into directories
-    StringTokenizer dirs = new StringTokenizer(baseDirList, ":");
-    int n = dirs.countTokens();
-    // add directories into array
-    baseDirs = new String[n];
-    for (int i = 0; i < n; i++) {
-      baseDirs[i] = dirs.nextToken();
-    }
-    // use authentication information
-    String useAuth = servletOp.tryToGetInitParam("use-authorization", "true");
-    if ((useAuth.indexOf("false") > 0)||(useAuth.indexOf("FALSE") > 0)) {
-      useAuthentication = false;
-    } else {
-      useAuthentication = true;
-      try {
-        // DB version
-        //authOp = new DBAuthOpsImpl(util);
-        // XML version
-        String cnfPath = servletOp.tryToGetInitParam("auth-file", "/docuserver/www/digitallibrary/WEB-INF/digilib-auth.xml");
-        authOp = new XMLAuthOps(util, cnfPath);
-      } catch (AuthOpException e) {
-        throw new ServletException(e);
-      }
-    }
-    // FileOps instance
-    fileOp = new FileOps(util);
-    // global DocuImage instance (don't reuse inside a request!)
-    globalImage = new JAIDocuImage(util);
-//    globalImage = new JIMIDocuImage(util);
-    //globalImage = new ImageLoaderDocuImage(util);
+	/** DigilibConfiguration instance */
+	DigilibConfiguration dlConfig;
+
+	/** use authorization database */
+	boolean useAuthorization = true;
+
+	/** AuthOps instance */
+	AuthOps authOp;
+
+	// EXPRIMENTAL
+	/** try to enlarge cropping area for "oblique" angles */
+	boolean wholeRotArea = false;
 
-  }
+	/**
+	 * Initialisation on first run.
+	 * 
+	 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
+	 */
+	public void init(ServletConfig config) throws ServletException {
+		super.init(config);
+
+		System.out
+				.println("***** Digital Image Library Image Scaler Servlet (version "
+						+ dlVersion + ") *****");
+		// say hello in the log file
+		logger
+				.info("***** Digital Image Library Image Scaler Servlet (version "
+						+ dlVersion + ") *****");
 
-  /**Process the HTTP Get request*/
-  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    util.dprintln(1, "The servlet has received a GET!");
-    processRequest(request, response);
-  }
-
-  /**Process the HTTP Post request*/
-  public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    util.dprintln(1, "The servlet has received a POST!");
-    processRequest(request, response);
-  }
-
-  /**Clean up resources*/
-  public void destroy() {
-  }
-
-/**********************************************************************
- *                       main request handler
- **********************************************************************/
-
-  void processRequest(HttpServletRequest request, HttpServletResponse response)
-    throws ServletException, IOException {
+		// get our ServletContext
+		ServletContext context = config.getServletContext();
+		// see if there is a Configuration instance
+		dlConfig = (DigilibConfiguration) context
+				.getAttribute("digilib.servlet.configuration");
+		if (dlConfig == null) {
+			// no Configuration
+			throw new ServletException("No Configuration!");
+		}
+		// set our AuthOps
+		useAuthorization = dlConfig.getAsBoolean("use-authorization");
+		authOp = (AuthOps) dlConfig.getValue("servlet.auth.op");
 
-    // time for benchmarking
-    long startTime = System.currentTimeMillis();
-    // output mime/type
-    String mimeType = "image/png";
-
-    /**
-     * parameters for a session
-     */
+		// DocuDirCache instance
+		dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+		denyImgFile = ServletOps.getFile((File) dlConfig.getValue("denied-image"), config);
+		errorImgFile = ServletOps.getFile((File) dlConfig.getValue("error-image"), config);
+		notfoundImgFile = ServletOps.getFile((File) dlConfig.getValue("notfound-image"), config);
+		sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed");
+		minSubsample = dlConfig.getAsFloat("subsample-minimum");
+		defaultQuality = dlConfig.getAsInt("default-quality");
+	}
 
-    // scale the image file to fit window size
-    boolean scaleToFit = true;
-    // use heuristics (GIF?) to scale or not
-    boolean forcedScale = false;
-    // try prescaled images first
-    boolean preScaledFirst = true;
-    // interpolation to use for scaling
-    int scaleQual = 0;
-    // send html error message (or image file)
-    boolean errorMsgHtml = false;
+	@Override
+	public void processRequest(HttpServletRequest request,
+			HttpServletResponse response) throws ServletException, ImageOpException {
 
-    /**
-     *  request parameter
-     */
+		
+		if (dlConfig == null) {
+			throw new ServletException("ERROR: No Configuration!");
+		}
+
+		accountlog.debug("request: " + request.getQueryString());
+		logger.debug("request: " + request.getQueryString());
+
+		
+
+		// define the job information
+		ImageJobInformation jobdeclaration = new ImageJobInformation();
 
-    // file/dir to load
-    String param_fn = servletOp.tryToGetParam("fn", "", request);
-    // page number
-    int param_pn = servletOp.tryToGetParam("pn", 1, request);
-    // destination image width
-    int param_dw = servletOp.tryToGetParam("dw", 300, request);
-    // destination image height
-    int param_dh = servletOp.tryToGetParam("dh", 400, request);
-    // relative area x_offset (0..1)
-    float param_wx = servletOp.tryToGetParam("wx", 0f, request);
-    // relative area y_offset
-    float param_wy = servletOp.tryToGetParam("wy", 0f, request);
-    // relative area width (0..1)
-    float param_ww = servletOp.tryToGetParam("ww", 1f, request);
-    // relative area height
-    float param_wh = servletOp.tryToGetParam("wh", 1f, request);
-    // scale factor (additional to dw/width, dh/height)
-    float param_ws = servletOp.tryToGetParam("ws", 1f, request);
-    // operation mode: flags separated by "+"
-    String param_mo = servletOp.tryToGetParam("mo", "", request);
-    // operation mode: "fit": always fit to page, "file": send as-is
-    if (param_mo.indexOf("fit") >= 0) {
-      scaleToFit = true;
-      forcedScale = true;
-    } else if (param_mo.indexOf("file") >= 0) {
-      scaleToFit = false;
-      forcedScale = true;
-    }
-    // operation mode: "errtxt": error message in html, "errimg": error image
-    if (param_mo.indexOf("errtxt") >= 0) {
-      errorMsgHtml = true;
-    } else if (param_mo.indexOf("errimg") >= 0) {
-      errorMsgHtml = false;
-    }
-    // operation mode: "q0" - "q2": interpolation quality
-    if (param_mo.indexOf("q0") >= 0) {
-      scaleQual = 0;
-    } else if (param_mo.indexOf("q1") >= 0) {
-      scaleQual = 1;
-    } else if (param_mo.indexOf("q2") >= 0) {
-      scaleQual = 2;
-    }
-    // operation mode: "lores": try to use scaled image, "hires": unscaled image
-    if (param_mo.indexOf("lores") >= 0) {
-      preScaledFirst = true;
-    } else if (param_mo.indexOf("hires") >= 0) {
-      preScaledFirst = false;
-    }
-
-    Utils.dprintln(1, "Parameter values: fn:"+param_fn+" pn:"+param_pn+" dw:"+param_dw+" dh:"+param_dh+" wx:"+param_wx+" wy:"+param_wy+" ww:"+param_ww+" wh:"+param_wh+" ws:"+param_ws+" mo:"+param_mo);
-
-    //"big" try for all file/image actions
-    try {
+		jobdeclaration.setWithRequest(request);
+		jobdeclaration.setConfig(dlConfig);
+	
+		
+		
+		
+		// TODO check, if file can be sent without transformations
+	
+		
+		
+		
+		OutputStream outputstream = null;
+		try {
+			outputstream =  response.getOutputStream();
+		} catch (IOException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+			logger.error(e1.getMessage());
+		}
+		
+		
+		DigilibWorker job=null;
+		try {
+			
+/*			logger.debug(outputstream.toString());
+			logger.debug(jobdeclaration.get_mimeType().toString());  
+			logger.debug(jobdeclaration.get_scaleQual().toString());
+			logger.debug(jobdeclaration.getAsFloat("rot")); 
+			logger.debug(jobdeclaration.getAsFloat("cont"));
+			logger.debug(jobdeclaration.getAsFloat("brgt"));
+			logger.debug(jobdeclaration.get_paramRGBM().toString());
+			logger.debug(jobdeclaration.get_paramRGBA().toString());
+			logger.debug("fileToLoad " + jobdeclaration.get_fileToLoad().toString());
+			logger.debug("scaleXY " + jobdeclaration.get_scaleXY());
+			logger.debug("get_outerUserImgArea " + jobdeclaration.get_outerUserImgArea().toString()); 
+			logger.debug("get_innerUserImgArea " + jobdeclaration.get_innerUserImgArea().toString()); 
+			logger.debug("minSubsample " + minSubsample);
+			logger.debug("get_wholeRotArea " + jobdeclaration.get_wholeRotArea());
+			logger.debug("get_forceType "+jobdeclaration.get_forceType()); */
+			
+			float scaleXY = jobdeclaration.get_scaleXY();
+			
+			long startTime = System.currentTimeMillis();
 
-    // DocuImage instance
-    DocuImage docuImage = new JAIDocuImage(util);
-//    DocuImage docuImage = new JIMIDocuImage(util);
-    //DocuImage docuImage = new ImageLoaderDocuImage(util);
-
-
-    /**
-     *  find the file to load/send
-     */
-
-    String loadPathName = "";
-    // if there's PathInfo, append
-    if (request.getPathInfo() != null) {
-      loadPathName += request.getPathInfo();
-    }
-    // append fn parameter
-    loadPathName += param_fn;
-    // if it's zoomed, try hires version (to be optimized...)
-    if ((param_ww < 1f) || (param_wh < 1f)) {
-      preScaledFirst = false;
-    }
+			
+			job = new DigilibImageWorker(dlConfig, 
+					outputstream ,
+					jobdeclaration.get_mimeType(), 
+					jobdeclaration.get_scaleQual(), 
+					//jobdeclaration,
+					jobdeclaration.getAsFloat("rot"), 
+					jobdeclaration.getAsFloat("cont"),
+					jobdeclaration.getAsFloat("brgt"), 
+					jobdeclaration.get_paramRGBM(), 
+					jobdeclaration.get_paramRGBA(), 
+					jobdeclaration.get_fileToLoad(), 
+					scaleXY,
+					jobdeclaration.get_outerUserImgArea(),
+					jobdeclaration.get_innerUserImgArea(),
+					minSubsample,
+					jobdeclaration.get_wholeRotArea(), 
+					jobdeclaration.get_forceType(),
+					jobdeclaration.get_hmir(),
+					jobdeclaration.get_vmir());
 
-    if (useAuthentication) {
-      // check permissions
-      List rolesRequired = authOp.rolesForPath(loadPathName, request);
-      if (rolesRequired != null) {
-        Utils.dprintln(1, "Role required: "+rolesRequired);
-        Utils.dprintln(2, "User: "+request.getRemoteUser());
-        if (! authOp.isRoleAuthorized(rolesRequired, request)) {
-          Utils.dprintln(1, "ERROR: access denied!");
-          if (errorMsgHtml) {
-            servletOp.htmlMessage("ERROR: Unauthorized access!", response);
-          } else {
-            docuImage.sendFile(denyImgFile, response);
-          }
-          return;
-        }
-      }
-    }
+			job.run();
 
-    // find the file
-    File fileToLoad = fileOp.getFileVariant(baseDirs, loadPathName, param_pn, preScaledFirst);
-
-    Utils.dprintln(1, "Loading: "+fileToLoad);
-
-    // get the source image type (if it's known)
-    mimeType = fileOp.mimeForFile(fileToLoad);
-
-    // if not forced and source is GIF/PNG then send-as-is if not zoomed
-    if((!forcedScale && (mimeType == "image/gif" || mimeType == "image/png")
-        && (param_ww == 1f) && (param_wh == 1f)) || (forcedScale && !scaleToFit)) {
-
-      Utils.dprintln(1, "Sending File as is.");
+			if (job.hasError()) {
+				throw new ImageOpException(job.getError().toString());
+			}
 
-      docuImage.sendFile(fileToLoad, response);
-
-      Utils.dprintln(1, "Done in "+(System.currentTimeMillis()-startTime)+"ms");
-      return;
-    }
-
-    // load file
-    docuImage.loadImage(fileToLoad);
-
-    /**
-     *  crop and scale the image
-     */
-
-    // get size
-    int imgWidth = docuImage.getWidth();
-    int imgHeight = docuImage.getHeight();
-
-    util.dprintln(2, "IMG: "+imgWidth+"x"+imgHeight);
-    util.dprintln(2, "time "+(System.currentTimeMillis()-startTime)+"ms");
+			try {
+				outputstream.flush();
+				logger.debug("Job Processing Time: "+ (System.currentTimeMillis()-startTime) + "ms");
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+				logger.error(e.getMessage());
+				response.sendError(1);
+			}
 
-    // calculate absolute from relative coordinates
-    float areaXoff = param_wx * imgWidth;
-    float areaYoff = param_wy * imgHeight;
-    float areaWidth = param_ww * imgWidth;
-    float areaHeight = param_wh * imgHeight;
-    // calculate scaling factors
-    float scaleX = param_dw / areaWidth * param_ws;
-    float scaleY = param_dh / areaHeight * param_ws;
-    float scaleXY = (scaleX > scaleY) ? scaleY : scaleX;
+			
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			logger.error(e.getClass()+": "+ e.getMessage());
+			//response.sendError(1);
+		} catch (ImageOpException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			logger.error(e.getClass()+": "+ e.getMessage());
+			//response.sendError(1);
+		}
 
-    util.dprintln(1, "Scale "+scaleXY+"("+scaleX+","+scaleY+") on "+areaXoff+","+areaYoff+" "+areaWidth+"x"+areaHeight);
+		
+		
 
-    // fit area to image
-    areaWidth = (areaXoff + areaWidth > imgWidth) ? imgWidth - areaXoff : areaWidth;
-    areaHeight = (areaYoff + areaHeight > imgHeight) ? imgHeight - areaYoff : areaHeight;
-
-    util.dprintln(2, "cropped: "+areaXoff+","+areaYoff+" "+areaWidth+"x"+areaHeight);
-
-    // check image parameters
-    if ((areaWidth < 1)||(areaHeight < 1)
-       ||(scaleXY * areaWidth < 2)||(scaleXY * areaHeight < 2)) {
-      Utils.dprintln(1, "ERROR: invalid scale parameter set!");
-      throw new ImageOpException("Invalid scale parameter set!");
-    }
-
-    // crop and scale image
-    docuImage.cropAndScale((int)areaXoff, (int)areaYoff, (int)areaWidth, (int)areaHeight,
-                            scaleXY, scaleQual);
-
-    util.dprintln(2, "time "+(System.currentTimeMillis()-startTime)+"ms");
+		
+		/*boolean errorMsgHtml = false;
+		
+		if(jobdeclaration.hasOption("mo","errtxt")){
+			errorMsgHtml = true;
+		} else if (jobdeclaration.hasOption("mo","errimg")) {
+			errorMsgHtml = true;
+		}
+		
+		
+		
+		*/
+				
+		
+	}
 
-    /**
-     *  write the resulting image
-     */
-
-    // setup output -- if source is JPG then dest will be JPG else it's PNG
-    if (mimeType != "image/jpeg") {
-      mimeType="image/png";
-    }
-
-    // write the image
-    docuImage.writeImage(mimeType, response);
-
-    util.dprintln(1, "Done in "+(System.currentTimeMillis()-startTime)+"ms");
-
-    /**
-     *  error handling
-     */
+	
+	
+	/**
+	 * Sends an error to the client as text or image.
+	 * 
+	 * @param asHTML
+	 * @param type
+	 * @param msg
+	 * @param response
+	 */
+	public void digilibError(boolean asHTML, int type, String msg,
+			HttpServletResponse response) {
+		try {
+			File img = null;
+			if (type == ERROR_AUTH) {
+				if (msg == null) {
+					msg = "ERROR: Unauthorized access!";
+				}
+				img = denyImgFile;
+			} else if (type == ERROR_FILE) {
+				if (msg == null) {
+					msg = "ERROR: Image file not found!";
+				}
+				img = notfoundImgFile;
+			} else {
+				if (msg == null) {
+					msg = "ERROR: Other image error!";
+				}
+				img = this.errorImgFile;
+			}
+			if (asHTML && (img != null)) {
+				ServletOps.htmlMessage(msg, response);
+			} else {
+				ServletOps.sendFile(img, null, response);
+			}
+		} catch (IOException e) {
+			logger.error("Error sending error!", e);
+		}
 
-    }//"big" try
-    catch (FileOpException e) {
-      util.dprintln(1, "ERROR: File IO Error: "+e);
-      try {
-        if (errorMsgHtml) {
-          servletOp.htmlMessage("ERROR: File IO Error: "+e, response);
-        } else {
-          globalImage.sendFile(errorImgFile, response);
-        }
-      } catch (FileOpException ex) {} // so we don't get a loop
-      return;
-    }
-    catch (AuthOpException e) {
-      Utils.dprintln(1, "ERROR: Authorization error: "+e);
-      try {
-        if (errorMsgHtml) {
-          servletOp.htmlMessage("ERROR: Authorization error: "+e, response);
-        } else {
-          globalImage.sendFile(errorImgFile, response);
-        }
-      } catch (FileOpException ex) {} // so we don't get a loop
-      return;
-    }
-    catch (ImageOpException e) {
-      Utils.dprintln(1, "ERROR: Image Error: "+e);
-      try {
-        if (errorMsgHtml) {
-          servletOp.htmlMessage("ERROR: Image Operation Error: "+e, response);
-        } else {
-          globalImage.sendFile(errorImgFile, response);
-        }
-      } catch (FileOpException ex) {} // so we don't get a loop
-      return;
-    }
+	}
 
-  }
+	public static String getVersion(){
+		return dlVersion;
+	}
+	
 
-}//Scaler class
+}