Mercurial > hg > digilib
changeset 1449:7f6b5e7e2afd
Merge from new_scaling branch
a2da0b5caedd6005d2be15c0507e4cb17d464d8e
author | robcast |
---|---|
date | Wed, 11 Nov 2015 20:33:10 +0100 |
parents | c4d32640c1be (current diff) a2da0b5caedd (diff) |
children | fa63f437d5c5 |
files | |
diffstat | 15 files changed, 610 insertions(+), 186 deletions(-) [+] |
line wrap: on
line diff
--- a/README.md Tue Nov 10 15:40:18 2015 +0100 +++ b/README.md Wed Nov 11 20:33:10 2015 +0100 @@ -18,6 +18,8 @@ * `digilib` facilitates cooperation of scholars over the internet and novel uses of source material by image annotations and stable references that can be embedded in URLs. +* `digilib` facilitates federation of image servers through a standards compliant + [IIIF](http://iiif.io) image API. * `digilib` is Open Source Software under the Lesser General Public License, jointly developed by the [Max-Planck-Institute for the History of Science](http://www.mpiwg-berlin.mpg.de),
--- a/common/src/main/java/digilib/conf/DigilibConfiguration.java Tue Nov 10 15:40:18 2015 +0100 +++ b/common/src/main/java/digilib/conf/DigilibConfiguration.java Wed Nov 11 20:33:10 2015 +0100 @@ -57,7 +57,7 @@ /** digilib version */ public static String getClassVersion() { - return "2.3.4a"; + return "2.3.5a"; } /* non-static getVersion for Java inheritance */
--- a/common/src/main/java/digilib/image/ImageJobDescription.java Tue Nov 10 15:40:18 2015 +0100 +++ b/common/src/main/java/digilib/image/ImageJobDescription.java Wed Nov 11 20:33:10 2015 +0100 @@ -1,5 +1,32 @@ package digilib.image; +/* + * #%L + * A class for storing the set of parameters necessary for scaling images with an ImageWorker. + * + * Digital Image Library servlet components + * + * %% + * Copyright (C) 2002 - 2015 MPIWG Berlin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + * Author: Robert Casties (robcast@users.sourceforge.de), + * Christopher Mielack (cmielack@mpiwg-berlin.mpg.de) + */ + import java.awt.geom.Rectangle2D; import java.io.IOException; @@ -67,6 +94,7 @@ */ public ImageJobDescription(DigilibConfiguration dlcfg) { super(30); + initParams(); dlConfig = dlcfg; dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); } @@ -138,13 +166,15 @@ * @param dlReq * @param dlcfg * @return + * @throws ImageOpException + * @throws IOException */ - public static ImageJobDescription getInstance(DigilibRequest dlReq, DigilibConfiguration dlcfg) { + public static ImageJobDescription getInstance(DigilibRequest dlReq, DigilibConfiguration dlcfg) throws IOException, ImageOpException { ImageJobDescription newMap = new ImageJobDescription(dlcfg); - newMap.initParams(); // add all params to this map newMap.params.putAll(dlReq.getParams()); newMap.initOptions(); + newMap.prepareScaleParams(); // add ImageJobDescription back into DigilibRequest dlReq.setJobDescription(newMap); return newMap; @@ -157,13 +187,15 @@ * @param pm * @param dlcfg * @return + * @throws ImageOpException + * @throws IOException */ - public static ImageJobDescription getInstance(ParameterMap pm, DigilibConfiguration dlcfg) { + public static ImageJobDescription getInstance(ParameterMap pm, DigilibConfiguration dlcfg) throws IOException, ImageOpException { ImageJobDescription newMap = new ImageJobDescription(dlcfg); - newMap.initParams(); // add all params to this map newMap.params.putAll(pm.getParams()); newMap.initOptions(); + newMap.prepareScaleParams(); return newMap; } @@ -171,8 +203,8 @@ /** * Prepare image scaling factors and coordinates. * + * Should be called by getInstance(). * Uses image size and user parameters. - * * Sets scaleX, scaleY, imgArea. * * @return @@ -182,10 +214,65 @@ public void prepareScaleParams() throws IOException, ImageOpException { // logger.debug("get_scaleXY()"); - /* + /* + * calculate scaling factors + */ + if (isScaleToFit()) { + /* + * scale to fit -- scale factor based on destination size dw/dh and user area + * using a uniform scale factor for x and y. + */ + imgArea = prepareScaleToFit(); + + } else if (isSqueezeToFit()) { + /* + * squeeze to fit -- scale factor based on destination size and user area + * + * uses separate scale factors for x and y + */ + imgArea = prepareSqueezeToFit(); + + } else if (isCropToFit()) { + /* + * crop to fit -- don't scale + */ + imgArea = prepareCropToFit(); + + } else if (isAbsoluteScale()) { + /* + * absolute scaling factor -- either original size, based on dpi, or absolute + */ + imgArea = prepareAbsoluteScale(); + + } else { + throw new ImageOpException("Unknown scaling mode!"); + } + + } + + /** + * Scale to fit: scale factor based on destination size dw/dh and user area. + * + * Uses a uniform scale factor for x and y. + * Sets ScaleX and ScaleY. + */ + protected Rectangle2D prepareScaleToFit() throws IOException { + /* + * prepare minimum source image size + * + * minSourceSize: w_min = dw * 1/ww + * + * Note: dw or dh can be empty (=0) + */ + float scale = (1 / Math.min(getWw(), getWh())); + minSourceSize = new ImageSize( + Math.round(getAsInt("dw") * scale), + Math.round(getAsInt("dh") * scale)); + + /* * get image region of interest */ - // size of the currently selected input image + // size of the currently selected input image (uses minSourceSize) imgSize = getImgSize(); // transform from relative [0,1] to image coordinates. double areaXf = getWx() * imgSize.getWidth(); @@ -199,121 +286,201 @@ long areaWidth = Math.round(areaWidthF); /* - * calculate scaling factors + * calculate scale factors */ - if (isScaleToFit()) { - /* - * scale to fit -- scaling factor based on destination size and user area - */ - scaleX = getDw() / (double) areaWidth; - scaleY = getDh() / (double) areaHeight; - if (scaleX == 0) { - // dw undefined - scaleX = scaleY; - } else if (scaleY == 0) { - // dh undefined - scaleY = scaleX; + scaleX = getDw() / (double) areaWidth; + scaleY = getDh() / (double) areaHeight; + if (scaleX == 0) { + // dw undefined + scaleX = scaleY; + } else if (scaleY == 0) { + // dh undefined + scaleY = scaleX; + } else { + // use the smaller factor to get fit-in-box + if (scaleX > scaleY) { + scaleX = scaleY; + if (hasOption("fill")) { + // fill mode uses whole destination rect + // TODO: should we center, clip or shift the area? + areaWidth = (long) (getDw() / scaleX); + } } else { - // use the smaller factor to get fit-in-box - if (scaleX > scaleY) { - scaleX = scaleY; - if (hasOption("fill")) { - // fill mode uses whole destination rect - // TODO: should we center, clip or shift the area? - areaWidth = (long) (getDw() / scaleX); - } - } else { - scaleY = scaleX; - if (hasOption("fill")) { - // fill mode uses whole destination rect - // TODO: should we center, clip or shift the area? - areaHeight = (long) (getDh() / scaleY); - } - } - } - - } else if (isSqueezeToFit()) { - /* - * squeeze to fit -- scaling factor based on destination size and user area - */ - scaleX = getDw() / (double) areaWidth; - scaleY = getDh() / (double) areaHeight; - - } else if (isCropToFit()){ - /* - * crop to fit -- don't scale - */ - areaWidth = getDw(); - areaHeight = getDh(); - scaleX = 1d; - scaleY = 1d; - - } else if (isAbsoluteScale()) { - /* - * absolute scaling factor -- either original size, based on dpi, or absolute - */ - if (hasOption("osize")) { - /* - * get original resolution from metadata - */ - imageSet.checkMeta(); - double origResX = imageSet.getResX(); - double origResY = imageSet.getResY(); - if ((origResX == 0) || (origResY == 0)) { - throw new ImageOpException("Missing image DPI information!"); - } - double ddpix = getAsFloat("ddpix"); - double ddpiy = getAsFloat("ddpiy"); - if (ddpix == 0 || ddpiy == 0) { - double ddpi = getAsFloat("ddpi"); - if (ddpi == 0) { - throw new ImageOpException("Missing display DPI information!"); - } else { - ddpix = ddpi; - ddpiy = ddpi; - } - } - // calculate absolute scale factor - scaleX = ddpix / origResX; - scaleY = ddpiy / origResY; - - } else { - /* - * explicit absolute scale factor - */ - double scaleXY = (double) getAsFloat("scale"); - scaleX = scaleXY; - scaleY = scaleXY; - // use original size if no destination size given - if (getDw() == 0 && getDh() == 0) { - paramDW = (int) areaWidth; - paramDH = (int) areaHeight; + scaleY = scaleX; + if (hasOption("fill")) { + // fill mode uses whole destination rect + // TODO: should we center, clip or shift the area? + areaHeight = (long) (getDh() / scaleY); } } + } + + return new Rectangle2D.Double(areaX, areaY, areaWidth, areaHeight); + } + + /** + * Squeeze to fit: scale factor based on destination size and user area. + * + * Uses separate scale factors for x and y + * Sets ScaleX and ScaleY. + */ + protected Rectangle2D prepareSqueezeToFit() throws IOException { + /* + * calculate minimum source size + * + * w_min = dw * 1/ww + */ + minSourceSize = new ImageSize( + Math.round(getAsInt("dw") / getWw()), + Math.round(getAsInt("dh") / getWh())); + + /* + * get image region of interest + */ + // size of the currently selected input image (uses minSourceSize) + imgSize = getImgSize(); + // transform from relative [0,1] to image coordinates. + double areaXf = getWx() * imgSize.getWidth(); + double areaYf = getWy() * imgSize.getHeight(); + double areaWidthF = getWw() * imgSize.getWidth(); + double areaHeightF = getWh() * imgSize.getHeight(); + // round to pixels + long areaX = Math.round(areaXf); + long areaY = Math.round(areaYf); + long areaHeight = Math.round(areaHeightF); + long areaWidth = Math.round(areaWidthF); + + /* + * calculate scale factors + */ + scaleX = getDw() / (double) areaWidth; + scaleY = getDh() / (double) areaHeight; + + return new Rectangle2D.Double(areaX, areaY, areaWidth, areaHeight); + } + + + /** + * Absolute scale factor: either original size, based on dpi, or absolute. + * + * Uses a uniform scale factor for x and y. + * Sets ScaleX and ScaleY. + * @throws ImageOpException + */ + protected Rectangle2D prepareAbsoluteScale() throws IOException, ImageOpException { + /* + * minimum source size -- apply scale to hires size + */ + minSourceSize = getHiresSize().getScaled(getAsFloat("scale")); + + /* + * get image region of interest + */ + // size of the currently selected input image (uses minSourceSize) + imgSize = getImgSize(); + // transform from relative [0,1] to image coordinates. + double areaXf = getWx() * imgSize.getWidth(); + double areaYf = getWy() * imgSize.getHeight(); + double areaWidthF = getWw() * imgSize.getWidth(); + double areaHeightF = getWh() * imgSize.getHeight(); + // round to pixels + long areaX = Math.round(areaXf); + long areaY = Math.round(areaYf); + long areaHeight = Math.round(areaHeightF); + long areaWidth = Math.round(areaWidthF); + + /* + * absolute scale factor -- either original size, based on dpi, or absolute + */ + if (hasOption("osize")) { /* - * correct scaling factor if we use a pre-scaled image + * get original resolution from metadata */ - hiresSize = getHiresSize(); - if (imgSize.getWidth() != hiresSize.getWidth()) { - double preScale = (double) hiresSize.getWidth() / (double) imgSize.getWidth(); - scaleX *= preScale; - scaleY *= preScale; + imageSet.checkMeta(); + double origResX = imageSet.getResX(); + double origResY = imageSet.getResY(); + if ((origResX == 0) || (origResY == 0)) { + throw new ImageOpException("Missing image DPI information!"); } - areaWidth = Math.round(getDw() / scaleX); - areaHeight = Math.round(getDh() / scaleY); + double ddpix = getAsFloat("ddpix"); + double ddpiy = getAsFloat("ddpiy"); + if (ddpix == 0 || ddpiy == 0) { + double ddpi = getAsFloat("ddpi"); + if (ddpi == 0) { + throw new ImageOpException("Missing display DPI information!"); + } else { + ddpix = ddpi; + ddpiy = ddpi; + } + } + // calculate absolute scale factor + scaleX = ddpix / origResX; + scaleY = ddpiy / origResY; } else { - throw new ImageOpException("Unknown scaling mode!"); + /* + * explicit absolute scale factor + */ + double scaleXY = (double) getAsFloat("scale"); + scaleX = scaleXY; + scaleY = scaleXY; + // use original size if no destination size given + if (getDw() == 0 && getDh() == 0) { + paramDW = (int) areaWidth; + paramDH = (int) areaHeight; + } } + /* + * correct absolute scale factor if we use a pre-scaled image + */ + hiresSize = getHiresSize(); + if (imgSize.getWidth() != hiresSize.getWidth()) { + double preScale = (double) hiresSize.getWidth() / (double) imgSize.getWidth(); + scaleX *= preScale; + scaleY *= preScale; + } + areaWidth = Math.round(getDw() / scaleX); + areaHeight = Math.round(getDh() / scaleY); + + return new Rectangle2D.Double(areaX, areaY, areaWidth, areaHeight); + } + - /* - * set image area - */ - imgArea = new Rectangle2D.Double(areaX, areaY, areaWidth, areaHeight); + /** + * Crop to fit: don't scale. + * + * Sets ScaleX and ScaleY. + */ + protected Rectangle2D prepareCropToFit() throws IOException { + /* + * minimum source size = hires size + */ + minSourceSize = getHiresSize(); + + /* + * get image region of interest + */ + // size of the currently selected input image (uses minSourceSize) + imgSize = getImgSize(); + // transform from relative [0,1] to image coordinates. + double areaXf = getWx() * imgSize.getWidth(); + double areaYf = getWy() * imgSize.getHeight(); + // round to pixels + long areaX = Math.round(areaXf); + long areaY = Math.round(areaYf); + + /* + * crop to fit -- don't scale + */ + int areaWidth = getDw(); + int areaHeight = getDh(); + scaleX = 1d; + scaleY = 1d; + + return new Rectangle2D.Double(areaX, areaY, areaWidth, areaHeight); + } + - } - - /** * Returns the mime-type of the input. * @@ -373,6 +540,8 @@ /** * Returns the ImageInput to use. * + * uses getMinSourceSize(). + * * @return * @throws IOException */ @@ -532,45 +701,13 @@ */ public ImageSize getMinSourceSize() throws IOException { //logger.debug("getMinSourceSize()"); - if (minSourceSize != null) { - return minSourceSize; - } - - minSourceSize = new ImageSize(); - if (isScaleToFit()) { - /* - * scale to fit -- calculate minimum source size - * - * roughly: w_min = dw * 1/ww - * - * Note: dw or dh can be empty (=0) - */ - float scale = (1 / Math.min(getWw(), getWh())); - minSourceSize.setSize( - Math.round(getAsInt("dw") * scale), - Math.round(getAsInt("dh") * scale)); - - } else if (isSqueezeToFit()) { - /* - * squeeze to fit -- calculate minimum source size - * - * w_min = dw * 1/ww - */ - minSourceSize.setSize( - Math.round(getAsInt("dw") / getWw()), - Math.round(getAsInt("dh") / getWh())); - - } else if (isAbsoluteScale() && hasOption("ascale")) { - /* - * absolute scale -- apply scale to hires size - */ - minSourceSize = getHiresSize().getScaled(getAsFloat("scale")); - - } else { - /* - * clip or other -- source = hires size - */ - minSourceSize = getHiresSize(); + if (minSourceSize == null) { + // this should not happen, it may lead to a loop! + logger.warn("MinSourceSize is not set! Calling prepareScaleParams again."); + try { + prepareScaleParams(); + } catch (ImageOpException e) { + } } return minSourceSize; }
--- a/common/src/main/java/digilib/image/ImageWorker.java Tue Nov 10 15:40:18 2015 +0100 +++ b/common/src/main/java/digilib/image/ImageWorker.java Wed Nov 11 20:33:10 2015 +0100 @@ -78,7 +78,6 @@ docuImage.setQuality(jobinfo.getScaleQual()); // get area of interest and scale factor - jobinfo.prepareScaleParams(); Rectangle loadRect = jobinfo.getOuterImgArea().getBounds(); double scaleX = jobinfo.getScaleX(); double scaleY = jobinfo.getScaleY();
--- a/pdf/src/main/java/digilib/conf/PDFRequest.java Tue Nov 10 15:40:18 2015 +0100 +++ b/pdf/src/main/java/digilib/conf/PDFRequest.java Wed Nov 11 20:33:10 2015 +0100 @@ -1,5 +1,7 @@ package digilib.conf; +import java.io.IOException; + /* * #%L * A container class for storing a set of instruction parameters @@ -33,6 +35,7 @@ import org.apache.log4j.Logger; import digilib.image.ImageJobDescription; +import digilib.image.ImageOpException; import digilib.io.DocuDirectory; import digilib.io.FileOpException; import digilib.util.NumRange; @@ -72,9 +75,10 @@ * * @param dlcfg The DigilibConfiguration. * @param request - * @throws FileOpException + * @throws ImageOpException + * @throws IOException */ - public PDFRequest(HttpServletRequest request, DigilibConfiguration dlcfg) throws FileOpException { + public PDFRequest(HttpServletRequest request, DigilibConfiguration dlcfg) throws IOException, ImageOpException { super(30); dlConfig = dlcfg; initParams(); @@ -105,9 +109,10 @@ * Read the request object. * * @param request - * @throws FileOpException + * @throws ImageOpException + * @throws IOException */ - public void setWithRequest(HttpServletRequest request) throws FileOpException { + public void setWithRequest(HttpServletRequest request) throws IOException, ImageOpException { // read matching request parameters for the parameters in this map for (String k : params.keySet()) { if (request.getParameterMap().containsKey(k)) { @@ -145,7 +150,7 @@ } - public ImageJobDescription getImageJobInformation(){ + public ImageJobDescription getImageJobInformation() throws IOException, ImageOpException{ return ImageJobDescription.getInstance(this, dlConfig); }
--- a/pdf/src/main/java/digilib/pdf/PDFStreamWorker.java Tue Nov 10 15:40:18 2015 +0100 +++ b/pdf/src/main/java/digilib/pdf/PDFStreamWorker.java Wed Nov 11 20:33:10 2015 +0100 @@ -40,6 +40,7 @@ import digilib.image.DocuImage; import digilib.image.ImageJobDescription; +import digilib.image.ImageOpException; import digilib.image.ImageWorker; import digilib.conf.DigilibConfiguration; import digilib.conf.PDFRequest; @@ -85,9 +86,10 @@ * @throws InterruptedException * @throws ExecutionException * @throws IOException + * @throws ImageOpException */ protected OutputStream renderPDF() throws DocumentException, InterruptedException, - ExecutionException, IOException { + ExecutionException, IOException, ImageOpException { // create document object doc = new Document(PageSize.A4, 0, 0, 0, 0); PdfWriter docwriter = null; @@ -147,7 +149,13 @@ */ public Document addTitlePage(Document doc) throws DocumentException { PDFTitlePage titlepage = new PDFTitlePage(job_info); - doc.add(titlepage.getPageContents()); + try { + doc.add(titlepage.getPageContents()); + } catch (IOException e) { + throw new DocumentException(e); + } catch (ImageOpException e) { + throw new DocumentException(e); + } doc.newPage(); return doc; }
--- a/pdf/src/main/java/digilib/pdf/PDFTitlePage.java Tue Nov 10 15:40:18 2015 +0100 +++ b/pdf/src/main/java/digilib/pdf/PDFTitlePage.java Wed Nov 11 20:33:10 2015 +0100 @@ -40,6 +40,7 @@ import com.itextpdf.text.Paragraph; import digilib.conf.PDFRequest; +import digilib.image.ImageOpException; import digilib.io.FileOpException; import digilib.servlet.PDFCache; @@ -78,6 +79,10 @@ return new DigilibInfoReader(infoFn.getAbsolutePath()); } catch (FileOpException e) { logger.warn("info.xml not found"); + } catch (IOException e) { + logger.warn("image directory for info.xml not found"); + } catch (ImageOpException e) { + logger.warn("problem with parameters for info.xml"); } return null; } @@ -86,8 +91,10 @@ * generate iText-PDF-Contents for the title page * * @return + * @throws ImageOpException + * @throws IOException */ - public Element getPageContents(){ + public Element getPageContents() throws IOException, ImageOpException{ Paragraph content = new Paragraph(); content.setAlignment(Element.ALIGN_CENTER); @@ -165,7 +172,7 @@ return null; } - private String getTitle(){ + private String getTitle() throws IOException, ImageOpException { if(info_reader.hasInfo()) return info_reader.getAsString("title"); else
--- a/servlet/src/main/java/digilib/servlet/ServletOps.java Tue Nov 10 15:40:18 2015 +0100 +++ b/servlet/src/main/java/digilib/servlet/ServletOps.java Wed Nov 11 20:33:10 2015 +0100 @@ -379,6 +379,10 @@ logger.error("No response!"); return; } + + /* + * get image size + */ ImageSize size = null; try { // get original image size @@ -393,15 +397,24 @@ throw new ServletException("Unable to write error response!", e); } } + + /* + * get resource URL + */ String url = dlReq.getServletRequest().getRequestURL().toString(); if (url.endsWith("/info.json")) { url = url.substring(0, url.lastIndexOf("/info.json")); } else if (url.endsWith("/")) { url = url.substring(0, url.lastIndexOf("/")); } + + /* + * send response + */ response.setCharacterEncoding("UTF-8"); response.setContentType("application/json,application/ld+json"); PrintWriter writer; + logger.debug("sending info.json"); try { writer = response.getWriter(); writer.println("{");
--- a/servlet2/src/main/java/digilib/servlet/Scaler.java Tue Nov 10 15:40:18 2015 +0100 +++ b/servlet2/src/main/java/digilib/servlet/Scaler.java Wed Nov 11 20:33:10 2015 +0100 @@ -224,9 +224,7 @@ // parse request DigilibServletRequest dlRequest = new DigilibServletRequest(request, dlConfig); - // extract the job information - ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); - + // type of error reporting ErrMsg errMsgType = ErrMsg.IMAGE; if (dlRequest.hasOption("errtxt")) { @@ -236,10 +234,13 @@ } try { + // extract the job information + ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); + /* * check if we can fast-track without scaling */ - ImageInput fileToLoad = (ImageInput) jobTicket.getInput(); + ImageInput fileToLoad = jobTicket.getInput(); // check permissions if (useAuthorization) {
--- a/servlet2/src/main/java/digilib/servlet/ScalerNoThread.java Tue Nov 10 15:40:18 2015 +0100 +++ b/servlet2/src/main/java/digilib/servlet/ScalerNoThread.java Wed Nov 11 20:33:10 2015 +0100 @@ -55,7 +55,7 @@ private static final long serialVersionUID = 1450947819851623306L; /** digilib servlet version (for all components) */ - public static final String version = "2.3.1 nothread"; + public static final String version = DigilibServletConfiguration.getClassVersion() + " nothread"; /** servlet error codes */ public static enum Error { @@ -207,8 +207,6 @@ // parse request DigilibServletRequest dlRequest = new DigilibServletRequest(request, dlConfig); - // extract the job information - ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); // type of error reporting ErrMsg errMsgType = ErrMsg.IMAGE; @@ -219,6 +217,8 @@ } try { + // extract the job information + ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); /* * check if we can fast-track without scaling */
--- a/servlet3/src/main/java/digilib/servlet/Scaler.java Tue Nov 10 15:40:18 2015 +0100 +++ b/servlet3/src/main/java/digilib/servlet/Scaler.java Wed Nov 11 20:33:10 2015 +0100 @@ -237,14 +237,6 @@ // parse request DigilibServletRequest dlRequest = new DigilibServletRequest(request, dlConfig); - // extract the job information - final ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); - - // handle the info-request - if (dlRequest.hasOption("info")) { - ServletOps.sendIiifInfo(dlRequest, response, logger); - return; - } // type of error reporting ErrMsg errMsgType = defaultErrMsgType; @@ -256,17 +248,21 @@ errMsgType = ErrMsg.CODE; } - // error out if request was bad - if (dlRequest.errorMessage != null) { - digilibError(errMsgType, Error.UNKNOWN, dlRequest.errorMessage, response); - return; - } - try { - /* - * get the input file - */ - ImageInput fileToLoad = (ImageInput) jobTicket.getInput(); + // extract the job information + final ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); + + // handle the info-request + if (dlRequest.hasOption("info")) { + ServletOps.sendIiifInfo(dlRequest, response, logger); + return; + } + + // error out if request was bad + if (dlRequest.errorMessage != null) { + digilibError(errMsgType, Error.UNKNOWN, dlRequest.errorMessage, response); + return; + } /* * check permissions @@ -280,6 +276,11 @@ } /* + * get the input file + */ + ImageInput fileToLoad = jobTicket.getInput(); + + /* * if requested, send image as a file */ if (sendFileAllowed && jobTicket.getSendAsFile()) {
--- a/webapp/pom.xml Tue Nov 10 15:40:18 2015 +0100 +++ b/webapp/pom.xml Wed Nov 11 20:33:10 2015 +0100 @@ -13,6 +13,10 @@ <description>The Digital Image Library - web application server and HTML and JS clients.</description> <url>http://digilib.sourceforge.net</url> <packaging>war</packaging> + + <properties> + <skipTests>true</skipTests> + </properties> <build> <pluginManagement> @@ -33,7 +37,15 @@ </includes> </configuration> </plugin> - </plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.19</version> + <configuration> + <skip>${skipTests}</skip> + </configuration> + </plugin> + </plugins> </pluginManagement> </build> <profiles> @@ -159,5 +171,40 @@ </dependency> </dependencies> </profile> + <profile> + <id>cors-filter</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <!-- external servlet filter to add CORS headers --> + <dependencies> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlets</artifactId> + <version>9.2.13.v20150730</version> + <!-- <version>9.3.5.v20151012</version> --> + </dependency> + </dependencies> + </profile> </profiles> + <dependencies> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>9.2.13.v20150730</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-http</artifactId> + <version>9.2.13.v20150730</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + </dependencies> </project>
--- a/webapp/src/main/webapp/WEB-INF/web-3.0.xml Tue Nov 10 15:40:18 2015 +0100 +++ b/webapp/src/main/webapp/WEB-INF/web-3.0.xml Wed Nov 11 20:33:10 2015 +0100 @@ -50,4 +50,18 @@ /Scaler/* </url-pattern> </servlet-mapping> + + <!-- add CORS headers --> + <filter> + <filter-name>CORS</filter-name> + <!-- use either Tomcat's or Jetty's filter class --> + <!-- <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> --> + <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> + <async-supported>true</async-supported> + </filter> + <filter-mapping> + <filter-name>CORS</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + </web-app>
--- a/webapp/src/main/webapp/WEB-INF/web-additional.xml Tue Nov 10 15:40:18 2015 +0100 +++ b/webapp/src/main/webapp/WEB-INF/web-additional.xml Wed Nov 11 20:33:10 2015 +0100 @@ -75,4 +75,17 @@ </form-login-config> </login-config> + <!-- add CORS headers --> + <filter> + <filter-name>CORS</filter-name> + <!-- use either Tomcat's or Jetty's filter class --> + <!-- <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> --> + <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> + <async-supported>true</async-supported> + </filter> + <filter-mapping> + <filter-name>CORS</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + </web-app>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webapp/src/test/java/digilib/servlet/ScalerTest.java Wed Nov 11 20:33:10 2015 +0100 @@ -0,0 +1,177 @@ +package digilib.servlet; + +/* + * #%L + * ScalerTest -- tests for the digilib Scaler servlet + * + * Digital Image Library servlet components + * + * %% + * Copyright (C) 2015 MPIWG Berlin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + * Author: Robert Casties (robcast@users.sourceforge.net) + */ + +import static org.junit.Assert.assertEquals; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import javax.imageio.ImageIO; + +import org.eclipse.jetty.http.HttpTester; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletTester; +import org.junit.BeforeClass; +import org.junit.Test; + +import digilib.conf.DigilibServlet3Configuration; + +/** + * ScalerTest -- tests for the digilib Scaler servlet + * + * @author casties + * + */ +public class ScalerTest { + + private static ServletTester tester; + + public static String testFileName = "xterm_color_chart"; + + @BeforeClass + public static void startServer() throws Exception { + tester = new ServletTester(); + ServletContextHandler ctx = tester.getContext(); + // set up ServletContext + ctx.setContextPath("/"); + ctx.setResourceBase("src/main/webapp"); + ctx.setClassLoader(ServletTester.class.getClassLoader()); + // add digilib ContextListener + DigilibServlet3Configuration dlConfig = new DigilibServlet3Configuration(); + ctx.addEventListener(dlConfig); + tester.addServlet(Scaler.class, "/Scaler/*"); + // start the servlet + tester.start(); + } + + /** + * Requests the image file testFileName from the Scaler with the given parameters and returns the image. + * + * Checks the returned content-type (if contentType != null). + * + * @param params + * @param contentType + * @return + * @throws Exception + * @throws IOException + */ + private BufferedImage loadImage(String params, String contentType) throws Exception, IOException { + // prepare request + HttpTester.Request request = HttpTester.newRequest(); + request.setMethod("GET"); + request.setHeader("Host", "tester"); // should be "tester" + request.setURI("/Scaler?fn="+testFileName+"&"+params); + request.setContent(""); + ByteBuffer reqBuf = request.generate(); + // get response + ByteBuffer respBuf = tester.getResponses(reqBuf); + // parse response + HttpTester.Response response = HttpTester.parseResponse(respBuf); + // should be 200 - OK + assertEquals("status code", 200, response.getStatus()); + // check content-type + if (contentType != null) { + String ct = response.getStringField("content-type"); + assertEquals("content-type", contentType, ct); + } + // load response as image + ByteArrayInputStream bis = new ByteArrayInputStream(response.getContentBytes()); + BufferedImage img = ImageIO.read(bis); + return img; + } + + /** + * Test scaling with mo=fit + * @throws Exception + */ + @Test + public void testScaleFit() throws Exception { + BufferedImage img = loadImage("ww=0.0836&wh=0.0378&wx=0&wy=0.961&dw=173&dh=235&mo=fit,errcode", null); + assertEquals("height", 125, img.getHeight()); + assertEquals("width", 173, img.getWidth()); + } + + /** + * Test scaling with mo=squeeze + * @throws Exception + */ + @Test + public void testScaleSqueeze() throws Exception { + BufferedImage img = loadImage("ww=0.0836&wh=0.0378&wx=0&wy=0.961&dw=173&dh=235&mo=squeeze,errcode", null); + assertEquals("height", 235, img.getHeight()); + assertEquals("width", 173, img.getWidth()); + } + + /** + * Test scaling with mo=clip + * @throws Exception + */ + @Test + public void testScaleClip() throws Exception { + BufferedImage img = loadImage("ww=0.0836&wh=0.0378&wx=0&wy=0.961&dw=173&dh=235&mo=clip,errcode", null); + assertEquals("height", 60, img.getHeight()); + assertEquals("width", 173, img.getWidth()); + } + + /** + * Test scaling with mo=ascale + * @throws Exception + */ + @Test + public void testScaleAbsolute() throws Exception { + BufferedImage img = loadImage("mo=ascale&scale=0.1&mo=errcode", null); + assertEquals("height", 154, img.getHeight()); + assertEquals("width", 96, img.getWidth()); + } + + /** + * Test forced image type with mo=jpg + * @throws Exception + */ + @Test + public void testTypeJpg() throws Exception { + BufferedImage img = loadImage("ww=0.0836&wh=0.0378&wx=0&wy=0.961&dw=173&dh=235&mo=jpg,errcode", "image/jpeg"); + int px = img.getRGB(100, 100); + assertEquals("pixel color", -8421505, px); + } + + /** + * Test forced image type with mo=png + * @throws Exception + */ + @Test + public void testTypePng() throws Exception { + BufferedImage img = loadImage("ww=0.0836&wh=0.0378&wx=0&wy=0.961&dw=173&dh=235&mo=png,errcode", "image/png"); + int px = img.getRGB(100, 100); + assertEquals("pixel color", -8421505, px); + } + + +}