changeset 547:e1094c5ec032 digilibPDF

more cleanup and refactoring
author robcast
date Fri, 10 Dec 2010 09:35:09 +0100
parents e7c29b587829
children bc9196347188
files client/digitallibrary/digimage.jsp servlet/src/digilib/servlet/DigilibImageWorker1.java servlet/src/digilib/servlet/DigilibPDFWorker.java servlet/src/digilib/servlet/DigilibRequest.java servlet/src/digilib/servlet/DocumentBean.java servlet/src/digilib/servlet/ImageJobDescription.java servlet/src/digilib/servlet/ImageJobInformation.java servlet/src/digilib/servlet/ImageWorker.java servlet/src/digilib/servlet/OptionsSet.java servlet/src/digilib/servlet/PDFFileWorker.java servlet/src/digilib/servlet/PDFJobInformation.java servlet/src/digilib/servlet/PDFStreamWorker.java servlet/src/digilib/servlet/Parameter.java servlet/src/digilib/servlet/ParameterMap.java servlet/src/digilib/servlet/Scaler.java
diffstat 15 files changed, 685 insertions(+), 1082 deletions(-) [+]
line wrap: on
line diff
--- a/client/digitallibrary/digimage.jsp	Fri Oct 22 19:04:49 2010 +0200
+++ b/client/digitallibrary/digimage.jsp	Fri Dec 10 09:35:09 2010 +0100
@@ -26,7 +26,7 @@
 // add number of pages
 dlRequest.setValue("pt", docBean.getNumPages());
 // store objects for jsp:include
-pageContext.setAttribute("docBean", docBean, pageContext.REQUEST_SCOPE);
+pageContext.setAttribute("docBean", docBean, PageContext.REQUEST_SCOPE);
 %><html>
 <head>
     <title>Digital Document Library (L1)</title>
--- a/servlet/src/digilib/servlet/DigilibImageWorker1.java	Fri Oct 22 19:04:49 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,274 +0,0 @@
-/* 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.IOException;
-import java.io.OutputStream;
-
-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 DigilibImageWorker1 extends DigilibWorker1 {
-
-	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;
-
-
-	public DigilibImageWorker1(DigilibConfiguration dlConfig, OutputStream outstream, ImageJobInformation jobinfo) {
-		super();
-		
-		this.dlConfig = dlConfig;
-		this.outstream = outstream;
-		this.mimeType = jobinfo.get_mimeType();
-		this.scaleQual = jobinfo.get_scaleQual();
-		this.paramROT = jobinfo.getAsFloat("rot");
-		this.paramCONT = jobinfo.getAsFloat("cont");
-		this.paramBRGT = jobinfo.getAsFloat("brgt");
-		this.paramRGBM = jobinfo.get_paramRGBM();
-		this.paramRGBA = jobinfo.get_paramRGBA();
-		try {
-			this.fileToLoad = jobinfo.get_fileToLoad();
-			this.scaleXY = jobinfo.get_scaleXY();
-			this.outerUserImgArea = jobinfo.get_outerUserImgArea();
-			this.innerUserImgArea = jobinfo.get_innerUserImgArea();
-		} catch (IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (ImageOpException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-		
-		this.minSubsample = dlConfig.getAsFloat("subsample-minimum");
-		this.wholeRotArea = jobinfo.get_wholeRotArea();
-		this.forceType = jobinfo.get_forceType();
-		this.hmir = jobinfo.get_hmir();
-		this.vmir = jobinfo.get_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();
-	}
-}
--- a/servlet/src/digilib/servlet/DigilibPDFWorker.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/DigilibPDFWorker.java	Fri Dec 10 09:35:09 2010 +0100
@@ -162,7 +162,7 @@
 	 */
 	public void addImage(int pn) {
 		// create ImageJobInformation
-		ImageJobInformation iji = job_info.getImageJobInformation();
+		ImageJobDescription iji = job_info.getImageJobInformation();
 		iji.setValue("pn", pn);
 		// create image worker
 		DigilibImageWorker1 image_worker = new DigilibImageWorker1(dlConfig, null, iji);
--- a/servlet/src/digilib/servlet/DigilibRequest.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/DigilibRequest.java	Fri Dec 10 09:35:09 2010 +0100
@@ -61,15 +61,9 @@
  */
 public class DigilibRequest extends ParameterMap {
 
-	private static final long serialVersionUID = -4707707539569977901L;
-
-	//private Logger logger = Logger.getLogger(this.getClass());
+	protected DocuImage image; // internal DocuImage instance for this request
 
-	private boolean boolRDF = false; // use RDF Parameters
-
-	private DocuImage image; // internal DocuImage instance for this request
-
-	private ServletRequest servletRequest; // internal ServletRequest
+	protected ServletRequest servletRequest; // internal ServletRequest
 
 	/** Creates a new instance of DigilibRequest and sets default values. */
 	public DigilibRequest() {
@@ -100,7 +94,7 @@
 		// scale factor
 		newParameter("ws", new Float(1), null, 's');
 		// special options like 'fit' for gifs
-		newParameter("mo", "", null, 's');
+		newParameter("mo", this.options, null, 's');
 		// rotation angle (degree)
 		newParameter("rot", new Float(0), null, 's');
 		// contrast enhancement factor
@@ -328,7 +322,7 @@
 	public String getAsString(int type) {
 		StringBuffer s = new StringBuffer(50);
 		// go through all values
-		for (Parameter p: this.values()) {
+		for (Parameter p: params.values()) {
 			if ((type > 0) && (p.getType() != type)) {
 				// skip the wrong types
 				continue;
@@ -391,7 +385,7 @@
 		for (Enumeration<String> i = request.getParameterNames(); i.hasMoreElements();) {
 			String name = (String) i.nextElement();
 			// is this a known parameter?
-			if (this.containsKey(name)) {
+			if (params.containsKey(name)) {
 				Parameter p = (Parameter) this.get(name);
 				// internal parameters are not set
 				if (p.getType() == 'i') {
@@ -426,7 +420,7 @@
 				String name = URLDecoder.decode(nv[0], "UTF-8");
 				String val = URLDecoder.decode(nv[1], "UTF-8");
 				// is this a known parameter?
-				if (this.containsKey(name)) {
+				if (params.containsKey(name)) {
 					Parameter p = (Parameter) this.get(name);
 					// internal parameters are not set
 					if (p.getType() == 'i') {
@@ -447,6 +441,7 @@
 	/**
 	 * Test if option string <code>opt</code> is set. Checks if the substring
 	 * <code>opt</code> is contained in the options string <code>param</code>.
+	 * Deprecated! use hasOption(String opt) for "mo"-options.
 	 * 
 	 * @param opt
 	 *            Option string to be tested.
@@ -523,10 +518,6 @@
 		setValue("docu.image", image);
 	}
 
-	public boolean isRDF() {
-		return boolRDF;
-	}
-
 	/**
 	 * @return
 	 */
--- a/servlet/src/digilib/servlet/DocumentBean.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/DocumentBean.java	Fri Dec 10 09:35:09 2010 +0100
@@ -211,7 +211,7 @@
 		// get original pixel size
 		ImageFile origfile = fileset.getBiggest();
 		// check image for size if mo=hires
-		if ((! origfile.isChecked())&&dlRequest.hasOption("mo", "hires")) {
+		if ((! origfile.isChecked())&&dlRequest.hasOption("hires")) {
 			logger.debug("pre-checking image!");
 			ImageOps.checkFile(origfile);
 		}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/ImageJobDescription.java	Fri Dec 10 09:35:09 2010 +0100
@@ -0,0 +1,483 @@
+package digilib.servlet;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+import digilib.image.ImageOpException;
+import digilib.image.ImageOps;
+import digilib.image.ImageSize;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirectory;
+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.  
+ * 
+ * This contains the functionality formerly found in Scaler, processRequest, only factorized.
+ * 
+ * TODO clean up...
+ * 
+ * @author cmielack, casties
+ *
+ */
+
+public class ImageJobDescription 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;
+	DocuDirectory fileDir = null;
+	String filePath = null;
+	ImageSize expectedSourceSize = null;
+	Float scaleXY = null;
+	Rectangle2D userImgArea = null;
+	Rectangle2D outerUserImgArea= null;
+	Boolean imageSendable = null;
+	String mimeType;
+	Integer paramDW;
+	Integer paramDH;
+
+	/** create empty ImageJobDescription.
+	 * @param dlcfg
+	 */
+	public ImageJobDescription(DigilibConfiguration dlcfg) {
+		super(30);
+		dlConfig = dlcfg;
+		// 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", this.options, 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');
+	}
+
+
+	/** Constructor using another ParameterMap.
+	 * Clones internal Maps only!
+	 * @param otherMap
+	 * @param dlcfg
+	 */
+	public ImageJobDescription(ParameterMap otherMap, DigilibConfiguration dlcfg) {
+		super(otherMap);
+		dlConfig = dlcfg;
+	}
+	
+	
+	public String getMimeType() throws IOException {
+		if (mimeType == null) {
+			fileToLoad = getFileToLoad();
+			if(! fileToLoad.isChecked()){
+				ImageOps.checkFile(fileToLoad);
+			}
+			mimeType = fileToLoad.getMimetype();
+		}
+		return mimeType;
+	}
+	
+	public ImageFile getFileToLoad() throws IOException {
+		
+		if(fileToLoad == null){
+			fileset = getFileset();
+			
+			/* select a resolution */
+			if (getHiresOnly()) {
+				// get first element (= highest resolution)
+				fileToLoad = fileset.getBiggest();
+			} else if (getLoresOnly()) {
+				// enforced lores uses next smaller resolution
+				fileToLoad = fileset.getNextSmaller(getExpectedSourceSize());
+				if (fileToLoad == null) {
+					// this is the smallest we have
+					fileToLoad = fileset.getSmallest();
+				}
+			} else {
+				// autores: use next higher resolution
+				fileToLoad = fileset.getNextBigger(getExpectedSourceSize());
+				if (fileToLoad == null) {
+					// this is the highest we have
+					fileToLoad = fileset.getBiggest();
+				}
+			}
+			logger.info("Planning to load: " + fileToLoad.getFile());
+		}
+		
+		return fileToLoad;
+
+	}
+	
+	public DocuDirectory getFileDirectory() throws FileOpException{
+		if(fileDir==null){
+			DocuDirCache dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+	
+			fileDir = dirCache.getDirectory(getFilePath());
+			if (fileDir == null) {
+				throw new FileOpException("Directory " + getFilePath() + " not found.");
+			}
+		}
+		return fileDir;
+	}
+	
+    public ImageFileset getFileset() throws FileOpException{
+        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() {
+		if(filePath == null){
+			String s = this.getAsString("request.path");
+			s += this.getAsString("fn");
+			filePath = FileOps.normalName(s);
+		}
+		return filePath;
+	}
+
+	public boolean getHiresOnly(){
+		return hasOption("clip") || hasOption("hires");
+	}
+	
+	public boolean getLoresOnly(){
+		return hasOption("lores");
+	}
+
+	public boolean getScaleToFit() {
+		return !(hasOption("clip") || hasOption("osize") || hasOption("ascale"));
+	}
+
+	public boolean getAbsoluteScale(){
+		return hasOption("osize") || hasOption("ascale");
+	}
+	
+	
+	public ImageSize getExpectedSourceSize() throws IOException {
+		if (expectedSourceSize == null){
+			expectedSourceSize = new ImageSize();
+			if (getScaleToFit()) {
+				// scale to fit -- calculate minimum source size
+				float scale = (1 / Math.min(getAsFloat("ww"), getAsFloat("wh"))) * getAsFloat("ws");
+				expectedSourceSize.setSize((int) (getDw() * scale),
+						(int) (getDh() * scale));
+			} else if (getAbsoluteScale() && hasOption("ascale")) {
+				// absolute scale -- apply scale to hires size
+				expectedSourceSize = getHiresSize().getScaled(getAsFloat("scale"));
+			} else {
+				// clip to fit -- source = destination size
+				expectedSourceSize.setSize((int) (getDw() * getAsFloat("ws")),
+						(int) (getDh() * getAsFloat("ws")));
+			}
+		}
+		return expectedSourceSize;
+	}
+	
+	public ImageSize getHiresSize() throws IOException {
+		logger.debug("get_hiresSize()");
+
+		ImageSize hiresSize = null;
+		ImageFileset fileset = getFileset();
+		if (getAbsoluteScale()) {
+			ImageFile hiresFile = fileset.getBiggest();
+			if (!hiresFile.isChecked()) {
+				ImageOps.checkFile(hiresFile);
+			}
+			hiresSize = hiresFile.getSize();
+		}
+		return hiresSize;
+		
+	}
+	
+	/** Returns image scaling factor.
+	 * Uses image size and user parameters.
+	 * Modifies scaleXY, userImgArea. 
+	 * @return
+	 * @throws IOException
+	 * @throws ImageOpException
+	 */
+	public float getScaleXY() throws IOException, ImageOpException {
+		//logger.debug("get_scaleXY()");
+		if(scaleXY == null){
+			// coordinates and scaling
+			float areaWidth;
+			float areaHeight;
+			float ws = getAsFloat("ws");
+			ImageSize imgSize = getFileToLoad().getSize();
+			// 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
+			userImgArea = imgTrafo.createTransformedShape(
+					relUserArea).getBounds2D();
+	
+			if (getScaleToFit()) {
+				// calculate scaling factors based on inner user area
+				areaWidth = (float) userImgArea.getWidth();
+				areaHeight = (float) userImgArea.getHeight();
+				float scaleX = getDw() / areaWidth * ws;
+				float scaleY = getDh() / areaHeight * ws;
+				scaleXY = (scaleX > scaleY) ? scaleY : scaleX;
+			} else if (getAbsoluteScale()) {
+				// absolute scaling factor
+				if (hasOption("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 -- mean value
+					scaleXY = (sx + sy) / 2f;
+				} else {
+					scaleXY = getAsFloat("scale");
+				}
+				// we need to correct the factor if we use a pre-scaled image
+				ImageSize hiresSize = getHiresSize();
+				if (imgSize.getWidth() != hiresSize.getWidth()) {
+					scaleXY *= (float)hiresSize.getWidth() / (float)imgSize.getWidth();
+				}
+				areaWidth = getDw() / scaleXY * ws;
+				areaHeight = getDh() / scaleXY * ws;
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+			} else {
+				// crop to fit -- don't scale
+				areaWidth = getDw() * ws;
+				areaHeight = getDh() * ws;
+				// reset user area size
+				userImgArea.setRect(userImgArea.getX(), userImgArea.getY(),
+						areaWidth, areaHeight);
+				scaleXY = 1f;
+			}
+		}
+		return (float) scaleXY;
+	}
+	
+	public int getDw() throws IOException {
+		logger.debug("get_paramDW()");
+		if (paramDW == null) {
+
+			paramDW = getAsInt("dw");
+			paramDH = getAsInt("dh");
+
+			float imgAspect = getFileToLoad().getAspect();
+			if (paramDW == 0) {
+				// calculate dw
+				paramDW = Math.round(paramDH * imgAspect);
+				setValue("dw", paramDW);
+			} else if (paramDH == 0) {
+				// calculate dh
+				paramDH = Math.round(paramDW / imgAspect);
+				setValue("dh", paramDH);
+			}
+		}
+		return paramDW;
+	}
+	
+	public int getDh() throws IOException {
+		logger.debug("get_paramDH()");
+		if (paramDH == null) {
+			
+			paramDW = getAsInt("dw");
+			paramDH = getAsInt("dh");
+
+			float imgAspect = getFileToLoad().getAspect();
+			if (paramDW == 0) {
+				// calculate dw
+				paramDW = Math.round(paramDH * imgAspect);
+				setValue("dw", paramDW);
+			} else if (paramDH == 0) {
+				// calculate dh
+				paramDH = Math.round(paramDW / imgAspect);
+				setValue("dh", paramDH);
+			}
+		}
+		return paramDH;
+	}
+	
+	public Integer get_scaleQual(){
+		logger.debug("get_scaleQual()");
+		Integer qual = dlConfig.getAsInt("default-quality");
+		if(hasOption("q0"))
+			qual = 0;
+		else if(hasOption("q1"))
+			qual = 1;
+		else if(hasOption("q2"))
+			qual = 2;
+		return qual;
+	}
+
+	
+	public Rectangle2D getUserImgArea() throws IOException, ImageOpException{
+		if(userImgArea == null) {
+			// getScaleXY sets userImgArea
+			getScaleXY();
+		}
+		return userImgArea;		
+		
+	}
+	
+	public Rectangle2D getOuterUserImgArea() throws IOException, ImageOpException {
+		if(outerUserImgArea == null){
+			outerUserImgArea = getUserImgArea();
+			
+			// image size in pixels
+			ImageSize imgSize = getFileToLoad().getSize();
+			Rectangle2D imgBounds = new Rectangle2D.Float(0, 0, imgSize.getWidth(), 
+					imgSize.getHeight());
+			
+			// clip area at the image border
+			outerUserImgArea = outerUserImgArea.createIntersection(imgBounds);
+	
+			// check image parameters sanity
+			scaleXY = getScaleXY();
+			logger.debug("outerUserImgArea.getWidth()=" + outerUserImgArea.getWidth());
+			logger.debug("get_scaleXY() * outerUserImgArea.getWidth() = " + (scaleXY * outerUserImgArea.getWidth()));
+			
+			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!");
+			}
+		}
+		return outerUserImgArea;
+	}
+	
+	
+	public int getForceType(){
+		if(hasOption("jpg"))
+			return ImageOps.TYPE_JPEG;
+		if(hasOption("png"))
+			return ImageOps.TYPE_PNG;
+		
+		return ImageOps.TYPE_AUTO;
+	}
+	
+	public float[] getRGBM(){
+		float[] paramRGBM = null;//{0f,0f,0f};
+		Parameter p = params.get("rgbm");
+		if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+			return p.parseAsFloatArray("/");
+		}	
+		return paramRGBM;
+	}
+	
+	public float[] getRGBA(){
+		float[] paramRGBA =  null;//{0f,0f,0f};
+		Parameter p = params.get("rgba");
+		if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+			paramRGBA = p.parseAsFloatArray("/");
+		}
+		return paramRGBA;
+	}
+	
+	/** Has send-as-file been requested?
+	 * @return
+	 */
+	public boolean getSendAsFile(){
+		return hasOption("file")
+		|| hasOption("rawfile");
+	}
+	
+	/** Could the image be sent without processing?
+	 * Takes image type and additional image operations into account. 
+	 * Does not check requested size transformation.
+	 * @return
+	 * @throws IOException 
+	 */
+	public boolean isImageSendable() throws IOException {
+		// cached result?
+		if (imageSendable == null) {
+			String mimeType = getMimeType();
+			imageSendable = ( (mimeType.equals("image/jpeg")
+				        	|| mimeType.equals("image/png")
+				        	|| mimeType.equals("image/gif") )
+				        	&& 
+				        	!(hasOption("hmir")
+							|| hasOption("vmir") 
+							|| (getAsFloat("rot") != 0.0)
+							|| (getRGBM() != null) 
+							|| (getRGBA() != null)
+							|| (getAsFloat("cont") != 0.0) 
+							|| (getAsFloat("brgt") != 0.0)));
+		}
+		
+		return imageSendable;
+	}
+	
+	
+	public boolean isTransformRequired() throws IOException {
+		ImageSize is = getFileToLoad().getSize();
+		ImageSize ess = getExpectedSourceSize();
+		// nt = no transform required
+		boolean nt = isImageSendable() && (
+			// lores: send if smaller
+			(getLoresOnly() && is.isSmallerThan(ess))
+			// else send if it fits
+			|| (!(getLoresOnly() || getHiresOnly()) && is.fitsIn(ess)));
+		return ! nt;
+	}
+}
\ No newline at end of file
--- a/servlet/src/digilib/servlet/ImageJobInformation.java	Fri Oct 22 19:04:49 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,690 +0,0 @@
-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.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.DocuDirectory;
-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.  
- * 
- * This contains the functionality formerly found in Scaler, processRequest, only factorized.
- * 
- * TODO clean up...
- * 
- * @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;
-	DocuDirectory fileDir = null;
-	String filePath = null;
-	ImageSize expectedSourceSize = null;
-	Float scaleXY = null;
-	Rectangle2D userImgArea = null;
-	Rectangle2D outerUserImgArea= null;
-	Boolean imageSendable = null;
-//	Integer paramDW = null;
-//	Integer paramDH 
-	public ImageJobInformation(DigilibConfiguration dlcfg) {
-		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, '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');
-*/	
-		dlConfig = dlcfg;
-	}
-
-
-	
-	public void setWithRequest(HttpServletRequest request) {
-		for (String param : parameter_list){
-			if (request.getParameterMap().containsKey(param)){
-				this.setValueFromString(param, request.getParameter(param));
-			}
-		}
-	setValueFromString("request.path", ((HttpServletRequest) request).getPathInfo());
-	String[] baseurl_parts = ((HttpServletRequest) request).getRequestURL().toString().split("/");
-	String baseurl = "";
-	for(int i=0; i<baseurl_parts.length-2; i++){
-		baseurl += baseurl_parts[i]+"/";
-	}
-	setValueFromString("base.url", baseurl);
-	}
-	
-	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;
-	}
-	
-	
-	public String get_mimeType() {
-		String mimeType = "image/png";
-		
-		
-		ImageFile fileToLoad;
-		try {
-
-			fileToLoad = get_fileToLoad();
-			
-			if(!get_fileToLoad().isChecked()){
-				ImageOps.checkFile(fileToLoad);
-			}
-
-				
-			if(fileToLoad != null)
-				mimeType = fileToLoad.getMimetype();
-
-		} catch (IOException e) {
-			e.printStackTrace();
-		} catch (ImageOpException e) {
-			e.printStackTrace();
-		}
-
-		
-		return mimeType;
-	}
-	
-	public ImageFile get_fileToLoad() throws IOException, ImageOpException{
-		
-		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 DocuDirectory getFileDirectory() throws FileOpException{
-		if(fileDir==null){
-			DocuDirCache dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
-	
-			fileDir = dirCache.getDirectory(getFilePath());
-			if (fileDir == null) {
-				throw new FileOpException("Directory " + getFilePath() + " not found.");
-			}
-		}
-		return fileDir;
-	}
-	
-    public ImageFileset get_fileset() throws FileOpException{
-        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() {
-		if(filePath == null){
-			String s = this.getAsString("request.path");
-			s += this.getAsString("fn");
-			filePath = FileOps.normalName(s);
-		}
-		return filePath;
-	}
-
-	public boolean get_hiresOnly(){
-		return hasOption("mo","clip") || hasOption("mo","osize") || hasOption("mo","hires");
-	}
-	
-	public boolean get_loresOnly(){
-
-		return hasOption("mo","lores");
-	}
-
-	public boolean get_scaleToFit() {
-
-		return !(hasOption("mo","clip") || hasOption("mo","osize") || hasOption("mo","ascale"));
-	}
-
-	public boolean get_absoluteScale(){
-
-		return hasOption("mo","osize") || hasOption("mo","ascale");
-	}
-	
-	
-	public ImageSize get_expectedSourceSize() throws IOException, ImageOpException{
-
-		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 :-(
-				setValue("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{
-
-		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 = null;//{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 =  null;//{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");
-	}
-	
-    public float getRot(){
-        return getAsFloat("rot");
-    }
-    
-    public float getCont(){
-        return getAsFloat("cont");
-    }
-    
-    public float getBrgt(){
-        return getAsFloat("brgt");
-    }
-    
-	public boolean checkSendAsFile(){
-		return hasOption("mo", "file")
-		|| hasOption("mo", "rawfile");
-	}
-	
-	public boolean get_imageSendable(){
-		if(imageSendable==null){
-			String mimeType = get_mimeType();
-			imageSendable = ( (mimeType.equals("image/jpeg")
-				        	|| mimeType.equals("image/png")
-				        	|| mimeType.equals("image/gif") )
-				        	&& 
-				        	!(hasOption("mo", "hmir")
-							|| hasOption("mo", "vmir") 
-							|| (getAsFloat("rot") != 0)
-							|| (get_paramRGBM() != null) 
-							|| (get_paramRGBA() != null)
-							|| (getAsFloat("cont") != 0) 
-							|| (getAsFloat("brgt") != 0)));
-		}
-		
-		return imageSendable;
-	}
-	
-	
-	public boolean noTransformRequired(){
-		try {
-			return get_imageSendable() && ((get_loresOnly() && get_fileToLoad().getSize().isSmallerThan(
-					get_expectedSourceSize())) || (!(get_loresOnly() || get_hiresOnly()) && get_fileToLoad()
-							.getSize().fitsIn(expectedSourceSize)));
-		} catch (IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		} catch (ImageOpException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-		return false;
-	}
-}
\ No newline at end of file
--- a/servlet/src/digilib/servlet/ImageWorker.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/ImageWorker.java	Fri Dec 10 09:35:09 2010 +0100
@@ -23,9 +23,9 @@
     
     protected static Logger logger = Logger.getLogger(ImageWorker.class);
     private DigilibConfiguration dlConfig;
-    private ImageJobInformation jobinfo;
+    private ImageJobDescription jobinfo;
 
-    public ImageWorker(DigilibConfiguration dlConfig, ImageJobInformation jobinfo) {
+    public ImageWorker(DigilibConfiguration dlConfig, ImageJobDescription jobinfo) {
         super();
         this.dlConfig = dlConfig;
         this.jobinfo = jobinfo;
@@ -51,8 +51,8 @@
         // set interpolation quality
         docuImage.setQuality(jobinfo.get_scaleQual());
 
-        Rectangle loadRect = jobinfo.get_outerUserImgArea().getBounds();
-        float scaleXY = jobinfo.get_scaleXY();
+        Rectangle loadRect = jobinfo.getOuterUserImgArea().getBounds();
+        float scaleXY = jobinfo.getScaleXY();
         
         // use subimage loading if possible
         if (docuImage.isSubimageSupported()) {
@@ -72,7 +72,7 @@
                         + scaleXY);
             }
 
-            docuImage.loadSubimage(jobinfo.get_fileToLoad(), loadRect, (int) subsamp);
+            docuImage.loadSubimage(jobinfo.getFileToLoad(), loadRect, (int) subsamp);
 
             logger.debug("SUBSAMP: " + subsamp + " -> " + docuImage.getWidth()
                     + "x" + docuImage.getHeight());
@@ -81,7 +81,7 @@
 
         } else {
             // else load and crop the whole file
-            docuImage.loadImage(jobinfo.get_fileToLoad());
+            docuImage.loadImage(jobinfo.getFileToLoad());
             docuImage.crop((int) loadRect.getX(), (int) loadRect.getY(),
                     (int) loadRect.getWidth(), (int) loadRect.getHeight());
 
@@ -91,17 +91,17 @@
         // mirror image
         // operation mode: "hmir": mirror horizontally, "vmir": mirror
         // vertically
-        if (jobinfo.get_hmir()) {
+        if (jobinfo.hasOption("hmir")) {
             docuImage.mirror(0);
         }
-        if (jobinfo.get_vmir()) {
+        if (jobinfo.hasOption("vmir")) {
             docuImage.mirror(90);
         }
 
         // rotate image
-        if (jobinfo.getRot() != 0d) {
-            docuImage.rotate(jobinfo.getRot());
-            if (jobinfo.get_wholeRotArea()) {
+        if (jobinfo.getAsFloat("rot") != 0d) {
+            docuImage.rotate(jobinfo.getAsFloat("rot"));
+            /* if (jobinfo.get_wholeRotArea()) {
                 // crop to the inner bounding box
                 float xcrop = (float) (docuImage.getWidth() - jobinfo.get_innerUserImgArea().getWidth()
                         * scaleXY);
@@ -116,15 +116,15 @@
                             (int) (docuImage.getWidth() - xcrop),
                             (int) (docuImage.getHeight() - ycrop));
                 }
-            }
+            } */
 
         }
 
         // color modification
-        float[] paramRGBM = jobinfo.get_paramRGBM();
-        float[] paramRGBA = jobinfo.get_paramRGBA();
+        float[] paramRGBM = jobinfo.getRGBM();
+        float[] paramRGBA = jobinfo.getRGBA();
         if ((paramRGBM != null) || (paramRGBA != null)) {
-            // make shure we actually have two arrays
+            // make sure we actually have two arrays
             if (paramRGBM == null) {
                 paramRGBM = new float[3];
             }
@@ -140,8 +140,8 @@
         }
 
         // contrast and brightness enhancement
-        float paramCONT = jobinfo.getCont();
-        float paramBRGT = jobinfo.getBrgt();
+        float paramCONT = jobinfo.getAsFloat("cont");
+        float paramBRGT = jobinfo.getAsFloat("brgt");
         if ((paramCONT != 0f) || (paramBRGT != 0f)) {
             float mult = (float) Math.pow(2, paramCONT);
             docuImage.enhance(mult, paramBRGT);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/OptionsSet.java	Fri Dec 10 09:35:09 2010 +0100
@@ -0,0 +1,67 @@
+/**
+ * 
+ */
+package digilib.servlet;
+
+import java.util.HashSet;
+import java.util.StringTokenizer;
+
+/**
+ * @author casties
+ *
+ */
+@SuppressWarnings("serial")
+public class OptionsSet extends HashSet<String> {
+
+	protected String optionSep = ",";
+	
+	public OptionsSet() {
+		super();
+	}
+
+	/** Constructor with String of options.
+	 * @param s
+	 */
+	public OptionsSet(String s) {
+		super();
+		parseString(s);
+	}
+
+	/** Adds all options from String to Set.
+	 * @param s
+	 */
+	public void parseString(String s) {
+		if (s != null) {
+			StringTokenizer i = new StringTokenizer(s, optionSep);
+			while (i.hasMoreTokens()) {
+				String opt = i.nextToken();
+				this.add(opt);
+			}
+		}
+	}
+	
+	public boolean hasOption(String opt) {
+		return this.contains(opt);
+	}
+
+	public String toString() {
+		StringBuffer b = new StringBuffer();
+		for (String s: this) {
+			if (b.length() > 0) {
+				b.append(optionSep);
+			}
+			b.append(s);			
+		}
+		return b.toString();
+	}
+	
+	
+	public String getOptionSep() {
+		return optionSep;
+	}
+
+	public void setOptionSep(String optionSep) {
+		this.optionSep = optionSep;
+	}
+
+}
--- a/servlet/src/digilib/servlet/PDFFileWorker.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/PDFFileWorker.java	Fri Dec 10 09:35:09 2010 +0100
@@ -1,7 +1,17 @@
 package digilib.servlet;
 
+import java.io.OutputStream;
 import java.util.concurrent.Callable;
 
-public class PDFFileWorker extends PDFStreamWorker implements Callable<V> {
+import digilib.image.DocuImage;
+
+public class PDFFileWorker extends PDFStreamWorker implements Callable<OutputStream> {
+
+	public PDFFileWorker(DigilibConfiguration dlConfig,
+			OutputStream outputfile, PDFJobInformation job_info,
+			DigilibJobCenter<DocuImage> imageJobCenter) {
+		super(dlConfig, outputfile, job_info, imageJobCenter);
+		// TODO Auto-generated constructor stub
+	}
 
 }
--- a/servlet/src/digilib/servlet/PDFJobInformation.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/PDFJobInformation.java	Fri Dec 10 09:35:09 2010 +0100
@@ -27,7 +27,7 @@
 									   // (this should be redesigned later...)
 	
 	
-	ImageJobInformation image_info = null;
+	ImageJobDescription image_info = null;
 	DigilibConfiguration dlConfig = null;
 	NumRange pages = null;
 	/** gengeral logger for this class */
@@ -56,8 +56,8 @@
 	 * @param request
 	 */
 	public void setWithRequest(HttpServletRequest request) {
-		image_info = new ImageJobInformation(dlConfig);
-		image_info.setWithRequest(request);
+		image_info = new ImageJobDescription(dlConfig);
+		// FIXME: image_info.setWithRequest(request);
 		
 		for (String param : parameter_list){
 			if (request.getParameterMap().containsKey(param)){
@@ -108,8 +108,8 @@
 	}
 
 	
-	public ImageJobInformation getImageJobInformation(){
-		ImageJobInformation new_image_info = (ImageJobInformation) image_info.clone();
+	public ImageJobDescription getImageJobInformation(){
+		ImageJobDescription new_image_info = new ImageJobDescription(image_info, dlConfig);
 		return new_image_info;
 	}
 	
--- a/servlet/src/digilib/servlet/PDFStreamWorker.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/PDFStreamWorker.java	Fri Dec 10 09:35:09 2010 +0100
@@ -83,7 +83,7 @@
 		for (int p : pgs) {
 			logger.debug(" - adding Image " + p + " to " + outstream);
 			// create ImageJobInformation
-			ImageJobInformation iji = job_info.getImageJobInformation();
+			ImageJobDescription iji = job_info.getImageJobInformation();
 			iji.setValue("pn", p);
 			addImage(doc, iji);
 			logger.debug(" - done adding Image " + p + " to " + outstream);
@@ -135,7 +135,7 @@
 	 * @throws IOException
 	 * @throws DocumentException
 	 */
-	public Document addImage(Document doc, ImageJobInformation iji)
+	public Document addImage(Document doc, ImageJobDescription iji)
 			throws InterruptedException, ExecutionException, IOException,
 			DocumentException {
 		// create image worker
--- a/servlet/src/digilib/servlet/Parameter.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/Parameter.java	Fri Dec 10 09:35:09 2010 +0100
@@ -113,6 +113,11 @@
 			this.value = new File(val);
 			return true;
 		}
+		// set Options
+		if (c == OptionsSet.class) {
+			this.value = new OptionsSet(val);
+			return true;
+		}
 		// set Boolean if string == "true"
 		if (c == Boolean.class) {
 			this.value = new Boolean(val.compareToIgnoreCase("true") == 0);
--- a/servlet/src/digilib/servlet/ParameterMap.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/ParameterMap.java	Fri Dec 10 09:35:09 2010 +0100
@@ -30,22 +30,36 @@
  * @author casties
  *
  */
-public class ParameterMap extends HashMap<String, Parameter> {
+public class ParameterMap {
 
-	private static final long serialVersionUID = 1530820988748391313L;
-
+	protected HashMap<String, Parameter> params;
+	
+	protected OptionsSet options;
+	
 	/** Default constructor.
 	 * 
 	 */
 	public ParameterMap() {
-		super();
+		params = new HashMap<String, Parameter>();
+		options = new OptionsSet();
 	}
 
-	/** Construcotr with initial size.
+	/** Constructor with initial size.
 	 * @param arg0
 	 */
 	public ParameterMap(int arg0) {
-		super(arg0);
+		params = new HashMap<String, Parameter>(arg0);
+		options = new OptionsSet();
+	}
+
+	/** Shallow copy constructor.
+	 * Be warned that the maps are only cloned i.e. keys and values are shared!
+	 * @param pm
+	 */
+	@SuppressWarnings("unchecked")
+	public ParameterMap(ParameterMap pm) {
+		params = (HashMap<String, Parameter>) pm.params.clone();
+		options = (OptionsSet) pm.options.clone();
 	}
 
 	/** Get the Parameter with the corresponding key.
@@ -56,7 +70,7 @@
 	 * @return
 	 */
 	public Parameter get(String key) {
-		return super.get(key);
+		return params.get(key);
 	}
 
 	/** Get the Parameter with the corresponding key.
@@ -67,7 +81,7 @@
 	 * @return
 	 */
 	public Object getValue(String key) {
-		Parameter p = super.get(key);
+		Parameter p = params.get(key);
 		return (p != null) ? p.getValue() : null;
 	}
 	
@@ -79,7 +93,7 @@
 	 * @return
 	 */
 	public String getAsString(String key) {
-		Parameter p = super.get(key);
+		Parameter p = params.get(key);
 		return (p != null) ? p.getAsString() : null;
 	}
 
@@ -91,7 +105,7 @@
 	 * @return
 	 */
 	public int getAsInt(String key) {
-		Parameter p = super.get(key);
+		Parameter p = params.get(key);
 		return (p != null) ? p.getAsInt() : 0;
 	}
 
@@ -103,7 +117,7 @@
 	 * @return
 	 */
 	public float getAsFloat(String key) {
-		Parameter p = super.get(key);
+		Parameter p = params.get(key);
 		return (p != null) ? p.getAsFloat() : 0f;
 	}
 
@@ -115,7 +129,7 @@
 	 * @return
 	 */
 	public boolean getAsBoolean(String key) {
-		Parameter p = super.get(key);
+		Parameter p = params.get(key);
 		return (p != null) ? p.getAsBoolean() : false;
 	}
 
@@ -125,7 +139,7 @@
 	 * @return
 	 */
 	public boolean hasValue(String key) {
-		Parameter p = super.get(key);
+		Parameter p = params.get(key);
 		return (p != null) ? p.hasValue() : false;
 	}
 	
@@ -138,7 +152,7 @@
 	 * @return
 	 */
 	public Parameter put(String key, Parameter val) {
-		return super.put(key, val);
+		return params.put(key, val);
 	}
 
 	/** Add the Parameter val to the map, using val's name.
@@ -149,7 +163,7 @@
 	 * @return
 	 */
 	public Parameter put(Parameter val) {
-		return super.put(val.getName(), val);
+		return params.put(val.getName(), val);
 	}
 	
 	/** Add a new Parameter with name, default and value.
@@ -163,7 +177,7 @@
 	 */
 	public Parameter newParameter(String name, Object def, Object val) {
 		Parameter p = new Parameter(name, def, val);
-		return super.put(name, p);
+		return params.put(name, p);
 	}
 
 	/** Add a new Parameter with name, default, value and type.
@@ -178,7 +192,7 @@
 	 */
 	public Parameter newParameter(String name, Object def, Object val, int type) {
 		Parameter p = new Parameter(name, def, val, type);
-		return super.put(name, p);
+		return params.put(name, p);
 	}
 
 	/** Set the value of an existing parameter.
@@ -190,7 +204,7 @@
 	 * @return
 	 */
 	public boolean setValue(String key, Object val) {
-		Parameter p = get(key);
+		Parameter p = params.get(key);
 		if (p != null) {
 			p.setValue(val);
 			return true;
@@ -207,7 +221,7 @@
 	 * @return
 	 */
 	public boolean setValue(String key, int val) {
-		Parameter p = get(key);
+		Parameter p = params.get(key);
 		if (p != null) {
 			p.setValue(val);
 			return true;
@@ -224,7 +238,7 @@
 	 * @return
 	 */
 	public boolean setValue(String key, float val) {
-		Parameter p = get(key);
+		Parameter p = params.get(key);
 		if (p != null) {
 			p.setValue(val);
 			return true;
@@ -241,11 +255,19 @@
 	 * @return
 	 */
 	public boolean setValueFromString(String key, String val) {
-		Parameter p = get(key);
+		Parameter p = params.get(key);
 		if (p != null) {
 			p.setValueFromString(val);
 			return true;
 		}
 		return false;
 	}
+	
+	/** Returns of the option has been set.
+	 * @param opt
+	 * @return
+	 */
+	public boolean hasOption(String opt) {
+		return options.hasOption(opt);
+	}
 }
--- a/servlet/src/digilib/servlet/Scaler.java	Fri Oct 22 19:04:49 2010 +0200
+++ b/servlet/src/digilib/servlet/Scaler.java	Fri Dec 10 09:35:09 2010 +0100
@@ -24,10 +24,11 @@
 
 // TODO digilibError is not used anymore and may need to get reintegrated
 
+@SuppressWarnings("serial")
 public class Scaler extends RequestHandler {
 
     /** digilib servlet version (for all components) */
-    public static final String dlVersion = "1.8.1a";
+    public static final String dlVersion = "1.9.0a";
 
     /** general error code */
     public static final int ERROR_UNKNOWN = 0;
@@ -45,7 +46,7 @@
     DocuDirCache dirCache;
 
     /** Image executor */
-    DigilibJobCenter imageJobCenter;
+    DigilibJobCenter<DocuImage> imageJobCenter;
 
     /** authentication error image file */
     File denyImgFile;
@@ -68,40 +69,6 @@
     /** AuthOps instance */
     AuthOps authOp;
 
-    // EXPRIMENTAL
-    /** try to enlarge cropping area for "oblique" angles */
-    boolean wholeRotArea = false;
-
-    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;
-    }
-
-    /**
-     * 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;
-    }
-
     /**
      * Initialisation on first run.
      * 
@@ -136,7 +103,7 @@
         dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
 
         // Executor
-        imageJobCenter = (DigilibJobCenter) dlConfig
+        imageJobCenter = (DigilibJobCenter<DocuImage>) dlConfig
                 .getValue("servlet.worker.imageexecutor");
 
         denyImgFile = ServletOps.getFile(
@@ -148,7 +115,28 @@
         sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed");
     }
 
-    @Override
+    /** Returns modification time relevant to the request.
+     * 
+     * @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
+        DigilibRequest dlReq = new DigilibRequest(request);
+		// find the file(set)
+		DocuDirent f = dirCache.getFile(dlReq.getFilePath(),
+		        dlReq.getAsInt("pn"), FileOps.CLASS_IMAGE);
+        // find the requested file
+        if (f != null) {
+            DocuDirectory dd = (DocuDirectory) f.getParent();
+            mtime = dd.getDirMTime() / 1000 * 1000;
+        }
+        return mtime;
+    }
+
+
     public void processRequest(HttpServletRequest request,
             HttpServletResponse response) throws ServletException,
             ImageOpException {
@@ -161,21 +149,23 @@
         logger.debug("request: " + request.getQueryString());
         long startTime = System.currentTimeMillis();
 
-        // define the job information
-        ImageJobInformation jobdeclaration = new ImageJobInformation(dlConfig);
-        jobdeclaration.setWithRequest(request);
+        // parse request
+        DigilibRequest dlRequest = new DigilibRequest(request);
+        // extract the job information
+        ImageJobDescription jobTicket = new ImageJobDescription(dlRequest, dlConfig);
 
-        // DigilibWorker1 job=null;
         ImageWorker job = null;
         try {
+        	/*
+        	 *  check if we can fast-track without scaling
+        	 */
+            ImageFile fileToLoad = jobTicket.getFileToLoad();
 
-            ImageFile fileToLoad = jobdeclaration.get_fileToLoad();
-
-            /* check permissions */
+            // check permissions
             if (useAuthorization) {
                 // get a list of required roles (empty if no restrictions)
                 List<String> rolesRequired = authOp.rolesForPath(
-                        jobdeclaration.getFilePath(), request);
+                        jobTicket.getFilePath(), request);
                 if (rolesRequired != null) {
                     authlog.debug("Role required: " + rolesRequired);
                     authlog.debug("User: " + request.getRemoteUser());
@@ -188,20 +178,19 @@
             }
 
             // if requested, send image as a file
-            if (sendFileAllowed && jobdeclaration.checkSendAsFile()) {
+            if (sendFileAllowed && jobTicket.getSendAsFile()) {
                 String mt = null;
-                if (jobdeclaration.hasOption("mo", "rawfile")) {
+                if (jobTicket.hasOption("rawfile")) {
                     mt = "application/octet-stream";
                 }
                 logger.debug("Sending RAW File as is.");
+                ServletOps.sendFile(fileToLoad.getFile(), mt, response);
                 logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms");
-                ServletOps.sendFile(fileToLoad.getFile(), mt, response);
                 return;
             }
 
-            // if possible, send the image without actually having to transform
-            // it
-            if (jobdeclaration.noTransformRequired()) {
+            // if possible, send the image without actually having to transform it
+            if (! jobTicket.isTransformRequired()) {
                 logger.debug("Sending File as is.");
                 ServletOps.sendFile(fileToLoad.getFile(), null, response);
                 logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms");
@@ -215,7 +204,7 @@
                 return;
             }
             // create job
-            job = new ImageWorker(dlConfig, jobdeclaration);
+            job = new ImageWorker(dlConfig, jobTicket);
             // submit job
             Future<DocuImage> jobResult = imageJobCenter.submit(job);
             // wait for result