Mercurial > hg > digilib-old
changeset 495:190afddb103f
Added getImage method to ImageLoaderDocuImage;
added pdf output
author | cmielack |
---|---|
date | Wed, 11 Feb 2009 16:36:57 +0100 |
parents | 43509321f9d2 |
children | 1760b19df530 |
files | servlet/src/digilib/servlet/MakePDF.java servlet/src/digilib/servlet/PDFCache.java |
diffstat | 2 files changed, 1146 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/MakePDF.java Wed Feb 11 16:36:57 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 MakePDF 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/PDFCache.java Wed Feb 11 16:36:57 2009 +0100 @@ -0,0 +1,215 @@ +package digilib.servlet; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Hashtable; + +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; + + +/** + * PDF request manager with caching capabilities. + * This is a very early and dirty but functional version and will + * (hopefully soon) be replaced by a parallelized and more sophisticated one. + * + * @author cmielack + * + */ + +public class PDFCache extends HttpServlet { + + private static final long serialVersionUID = 1L; + + + private static int STATUS_NONEXISTENT = 0; // the document does not exist and is not under construction + private static int STATUS_DONE = 1; // the document exists and can be downloaded + private static int STATUS_PENDING = 2; // the document is "under construction" + private static int STATUS_ERROR = 3; // an error occurred while processing the request + + private static String cache_directory = "cache/"; // the path (relative to the tomcat base-directory) where cached + // files are stored + + private static String cache_hash_id = "digilib.servlet.PDFCache"; + + + private static Logger logger = Logger.getLogger("digilib.servlet"); + + + public void init(ServletConfig config){ + // initialize the PDFCache + + logger.debug("Initializing PDFCache"); + + try { + super.init(config); + } catch (ServletException e) { + e.printStackTrace(); + } + + ServletContext context = this.getServletContext(); + + context.setAttribute(cache_hash_id, new Hashtable<String,Integer>()); + + Hashtable<String,Integer> cache_hash = (Hashtable<String,Integer>) context.getAttribute(cache_hash_id); + + if (cache_hash==null){ + cache_hash = new Hashtable<String,Integer>(); + context.setAttribute(cache_hash_id, cache_hash); + } + + // search the cache-directory for existing files and fill them into the Hashtable as STATUS_DONE + File cache_dir = new File(cache_directory); + String[] cached_files = cache_dir.list(); + + + for (String file: cached_files){ + if (file.endsWith(".pdf")){ + logger.debug("cache found "+file); + cache_hash.put(file, 1); + } + } + + } + + + public String getDocumentId(HttpServletRequest request){ + // generate an unambiguous ID from the request (this is used for filenames etc) + // at this stage, the request-string is used + String id; + + id = request.getQueryString() + ".pdf"; + + return id; + } + + + public void doGet(HttpServletRequest request, HttpServletResponse response){ + + // get the status of the Document specified by the request ... + int status = this.getStatus(request); + + + if (status == STATUS_DONE) { + // ... and if the file already exists, send it ... + try { + sendFile(request,response); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + else if (status == STATUS_PENDING){ + // ... if it is in the works, notify the user about it ... + String redir_url = "abc.jsp"; + try { + response.sendRedirect(redir_url); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else if (status == STATUS_NONEXISTENT){ + // ... or else, generate the file and inform the user about the estimated generation-time + try { + this.createFile(request, response); + } catch (ServletException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else { + // if an error occurred ... + } + } + + public int getStatus(HttpServletRequest request){ + + String documentId = getDocumentId(request); + + ServletContext context = this.getServletContext(); + Hashtable<String,Integer> documentStatus = (Hashtable<String,Integer>) context.getAttribute(cache_hash_id); + + int status = STATUS_NONEXISTENT; + + if (documentStatus.containsKey(documentId)){ + status = documentStatus.get(documentId); + } + + return status; + } + + public void sendFile(HttpServletRequest request, HttpServletResponse response) throws IOException{ + // send the file specified by the request to the response + + String filename = getDocumentId(request); + File cached_file = null; + FileInputStream fis = null; + ServletOutputStream sos = null; + BufferedInputStream bis = null; + + try { + cached_file = new File(this.cache_directory + filename); + fis = new FileInputStream(cached_file); + sos = response.getOutputStream(); + bis = new BufferedInputStream(fis); + int bytes = 0; + + response.setContentType("application/pdf"); + response.addHeader("Content-Disposition", "attachment; filename="+filename); + response.setContentLength( (int) cached_file.length()); + + logger.debug("Sending document "+filename+" to the user."); + + while ((bytes = bis.read()) != -1){ + sos.write(bytes); + } + } + catch(Exception e){ + logger.error(e.getMessage()); + } + finally{ + // close all streams + if (fis != null) + fis.close(); + if (bis != null) + bis.close(); + if (sos != null) + sos.close(); + } + } + + public void createFile(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ + // use MakePDF to generate a new Document and put it into the cache-directory + + // get global instance of MakePDF + MakePDF mpdf = (MakePDF) this.getServletContext().getAttribute("digilib.servlet.MakePDF"); + + + if (mpdf==null){ + mpdf = new MakePDF(); + logger.debug("didn't find MakePDF-Object"); + } + + String filename = this.cache_directory + this.getDocumentId(request); + + logger.debug("createFile is going to create file "+filename); + + mpdf.doCreate(request,response,filename); // set the parameters and ... + mpdf.run(); // ... start generating the pdf + //new Thread(mpdf,"MakePDF").start(); + } +} \ No newline at end of file