# HG changeset patch
# User robcast
# Date 1447416548 -3600
# Node ID e7d94cfbec0b131cff7b596df7435f9c69e9b816
# Parent bb8296934cb2b18332e188546db1962d1f44bce0# Parent 9429bb9c3a4239e133b878a464d802bc8984ae60
Merge from default branch
9429bb9c3a4239e133b878a464d802bc8984ae60
diff -r bb8296934cb2 -r e7d94cfbec0b README.md
--- a/README.md Tue Nov 10 14:41:27 2015 +0100
+++ b/README.md Fri Nov 13 13:09:08 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),
diff -r bb8296934cb2 -r e7d94cfbec0b common/src/main/java/digilib/conf/DigilibConfiguration.java
--- a/common/src/main/java/digilib/conf/DigilibConfiguration.java Tue Nov 10 14:41:27 2015 +0100
+++ b/common/src/main/java/digilib/conf/DigilibConfiguration.java Fri Nov 13 13:09:08 2015 +0100
@@ -57,7 +57,7 @@
/** digilib version */
public static String getClassVersion() {
- return "2.3.4";
+ return "2.3.6a";
}
/* non-static getVersion for Java inheritance */
@@ -94,6 +94,8 @@
newParameter("default-errmsg-type", "image", null, 'f');
// prefix for IIIF image API paths (used by DigilibRequest)
newParameter("iiif-prefix", "IIIF", null, 'f');
+ // IIIF Image API version to support (mostly relevant for info.json)
+ newParameter("iiif-api-version", "2.0", null, 'f');
// character to use as slash-replacement in IIIF identifier part
newParameter("iiif-slash-replacement", "!", null, 'f');
}
diff -r bb8296934cb2 -r e7d94cfbec0b common/src/main/java/digilib/conf/DigilibRequest.java
--- a/common/src/main/java/digilib/conf/DigilibRequest.java Tue Nov 10 14:41:27 2015 +0100
+++ b/common/src/main/java/digilib/conf/DigilibRequest.java Fri Nov 13 13:09:08 2015 +0100
@@ -278,11 +278,13 @@
*
* path should be non-URL-decoded and have no leading slash.
*
+ * URI template:
+ * {scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
+ *
* @param path
* String with IIIF Image API path.
*
- * @see IIIF Image
- * API
+ * @see IIIF Image API
*/
public boolean setWithIiifPath(String path) {
if (path == null) {
@@ -315,7 +317,7 @@
}
}
/*
- * second parameter FN (encoded)
+ * second parameter identifier (encoded)
*/
if (query.hasMoreTokens()) {
token = getNextDecodedToken(query);
@@ -404,11 +406,16 @@
/**
* Populate a request from IIIF image API parameters.
- *
- * {scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}{.format}
+ *
+ * @see IIIF Image API
*
- * @see IIIF Image
- * API
+ * @param identifier
+ * @param region
+ * @param size
+ * @param rotation
+ * @param quality
+ * @param format
+ * @return
*/
public boolean setWithIiifParams(String identifier, String region, String size,
String rotation, String quality, String format) {
@@ -449,7 +456,7 @@
options.setOption("info");
return true;
} else if (region.equals("full")) {
- // full region -- default
+ // full image -- default
} else if (region.startsWith("pct:")) {
// pct:x,y,w,h -- region in % of original image
String[] parms = region.substring(4).split(",");
@@ -483,8 +490,8 @@
}
}
} else {
- // region omitted -- assume info request
- options.setOption("info");
+ // region omitted -- redirect to info request
+ options.setOption("redirect-info");
return true;
}
@@ -493,11 +500,16 @@
*/
if (size != null) {
if (size.equals("full")) {
- // full -- size of original
+ /*
+ * full -- size of original
+ */
options.setOption("ascale");
setValue("scale", 1f);
+
} else if (size.startsWith("pct:")) {
- // pct:n -- n% size of original
+ /*
+ * pct:n -- n% size of original
+ */
try {
float pct = Float.parseFloat(size.substring(4));
options.setOption("ascale");
@@ -507,17 +519,20 @@
logger.error(errorMessage+e);
return false;
}
+
} else {
- // w,h -- pixel size
+ /*
+ * w,h -- pixel size
+ */
try {
String[] parms = size.split(",", 2);
if (parms[0].length() > 0) {
// width param
if (parms[0].startsWith("!")) {
- // width (in digilib-like bounding box)
+ // !w,h width (in digilib-like bounding box)
setValueFromString("dw", parms[0].substring(1));
} else if (parms[1].length() == 0) {
- // width only
+ // w, width only
setValueFromString("dw", parms[0]);
} else {
// w,h -- according to spec, we should distort the image to match ;-(
@@ -546,6 +561,11 @@
* parameter rotation
*/
if (rotation != null) {
+ if (rotation.startsWith("!")) {
+ // !n -- mirror and rotate
+ options.setOption("hmir");
+ rotation = rotation.substring(1);
+ }
try {
float rot = Float.parseFloat(rotation);
setValue("rot", rot);
@@ -561,9 +581,9 @@
*/
if (quality != null) {
// quality param
- if (quality.equals("native") || quality.equals("color")) {
- // native is default anyway
- } else if (quality.equals("grey")) {
+ if (quality.equals("default") || quality.equals("native") || quality.equals("color")) {
+ // color is default anyway
+ } else if (quality.equals("gray") || quality.equals("grey")) {
setValueFromString("colop", "grayscale");
} else {
errorMessage = "Invalid quality parameter in IIIF path!";
diff -r bb8296934cb2 -r e7d94cfbec0b common/src/main/java/digilib/image/ImageJobDescription.java
--- a/common/src/main/java/digilib/image/ImageJobDescription.java Tue Nov 10 14:41:27 2015 +0100
+++ b/common/src/main/java/digilib/image/ImageJobDescription.java Fri Nov 13 13:09:08 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
+ * .
+ * #L%
+ * Author: Robert Casties (robcast@users.sourceforge.de),
+ * Christopher Mielack (cmielack@mpiwg-berlin.mpg.de)
+ */
+
import java.awt.geom.Rectangle2D;
import java.io.IOException;
@@ -56,6 +83,8 @@
protected Float paramWY = null;
protected Float paramWW = null;
protected Float paramWH = null;
+ protected float[] paramRGBM = null;
+ protected float[] paramRGBA = null;
protected DocuDirCache dirCache = null;
protected ImageSize hiresSize = null;
protected ImageSize imgSize = null;
@@ -67,6 +96,7 @@
*/
public ImageJobDescription(DigilibConfiguration dlcfg) {
super(30);
+ initParams();
dlConfig = dlcfg;
dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
}
@@ -138,13 +168,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 +189,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 +205,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 +216,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,123 +288,203 @@
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);
-
- }
-
/**
- * Returns the mime-type of the input.
+ * 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);
+ }
+
+
+ /**
+ * Return the mime-type of the input.
*
* @return
* @throws IOException
@@ -373,6 +542,8 @@
/**
* Returns the ImageInput to use.
*
+ * Note: uses getMinSourceSize().
+ *
* @return
* @throws IOException
*/
@@ -408,7 +579,7 @@
}
/**
- * Returns the DocuDirectory for the input (file).
+ * Return the DocuDirectory for the input (file).
*
* @return
* @throws FileOpException
@@ -425,7 +596,7 @@
}
/**
- * Returns the ImageSet to load.
+ * Return the ImageSet to load.
*
* @return
* @throws FileOpException
@@ -454,7 +625,7 @@
/**
- * Returns the file path name from the request.
+ * Return the file path name from the request.
*
* @return
*/
@@ -523,60 +694,28 @@
}
/**
- * Returns the minimum size the source image should have for scaling.
+ * Return the minimum size the source image should have for scaling.
*
- * This function is called by getInput(). It must not assume a selected input image!
+ * Note: this function is called by getInput(). It must not assume a selected input image!
*
* @return
* @throws IOException
*/
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;
}
/**
- * Returns the size of the highest resolution image.
+ * Return the size of the highest resolution image.
*
* @return
* @throws IOException
@@ -595,7 +734,7 @@
}
/**
- * Returns the size of the selected input image.
+ * Return the size of the selected input image.
*
* @return
* @throws IOException
@@ -638,40 +777,37 @@
}
/**
- * Returns the width of the destination image.
- * Uses dh parameter and aspect ratio if dw parameter is empty.
+ * Return the width of the destination image.
+ * Uses dw parameter.
*
* @return
- * @throws IOException
*/
- public int getDw() throws IOException {
+ public int getDw() {
//logger.debug("get_paramDW()");
if (paramDW == null) {
paramDW = getAsInt("dw");
- paramDH = getAsInt("dh");
}
return paramDW;
}
/**
- * Returns the height of the destination image.
- * Uses dw parameter and aspect ratio if dh parameter is empty.
+ * Return the height of the destination image.
+ * Uses dh parameter.
*
* @return
- * @throws IOException
*/
- public int getDh() throws IOException {
+ public int getDh() {
//logger.debug("get_paramDH()");
if (paramDH == null) {
- paramDW = getAsInt("dw");
paramDH = getAsInt("dh");
}
return paramDH;
}
/**
- * Returns the relative width of the image area.
+ * Return the relative width of the image area.
* Uses ww parameter.
+ * Converts ww in pixels to relative.
*
* @return
* @throws IOException
@@ -690,8 +826,9 @@
}
/**
- * Returns the relative height of the image area.
+ * Return the relative height of the image area.
* Uses wh parameter.
+ * Converts wh in pixels to relative.
*
* @return
* @throws IOException
@@ -710,8 +847,9 @@
}
/**
- * Returns the relative x-offset of the image area.
+ * Return the relative x-offset of the image area.
* Uses wx parameter.
+ * Converts wx in pixels to relative.
*
* @return
* @throws IOException
@@ -730,8 +868,9 @@
}
/**
- * Returns the relative y-offset of the image area.
+ * Return the relative y-offset of the image area.
* Uses wy parameter.
+ * Converts wy in pixels to relative.
*
* @return
* @throws IOException
@@ -750,7 +889,7 @@
}
/**
- * Returns image quality as an integer.
+ * Return image quality as an integer.
*
* @return
*/
@@ -785,7 +924,7 @@
}
/**
- * Returns the maximal area of the source image that will be used.
+ * Return the maximum area of the source image that will be used.
*
* This was meant to include extra pixels outside the
* imgArea when rotating by oblique angles but is not yet implemented.
@@ -828,10 +967,11 @@
* @return
*/
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("/");
+ if (paramRGBM == null) {
+ Parameter p = params.get("rgbm");
+ if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+ paramRGBM = p.parseAsFloatArray("/");
+ }
}
return paramRGBM;
}
@@ -842,10 +982,11 @@
* @return
*/
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("/");
+ if (paramRGBA == null) {
+ Parameter p = params.get("rgba");
+ if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) {
+ paramRGBA = p.parseAsFloatArray("/");
+ }
}
return paramRGBA;
}
diff -r bb8296934cb2 -r e7d94cfbec0b common/src/main/java/digilib/image/ImageWorker.java
--- a/common/src/main/java/digilib/image/ImageWorker.java Tue Nov 10 14:41:27 2015 +0100
+++ b/common/src/main/java/digilib/image/ImageWorker.java Fri Nov 13 13:09:08 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();
diff -r bb8296934cb2 -r e7d94cfbec0b common/src/main/java/digilib/io/ImageSet.java
--- a/common/src/main/java/digilib/io/ImageSet.java Tue Nov 10 14:41:27 2015 +0100
+++ b/common/src/main/java/digilib/io/ImageSet.java Fri Nov 13 13:09:08 2015 +0100
@@ -35,6 +35,9 @@
/**
* Set of ImageInputs of the same image in different resolutions.
*
+ * The images are be added in the order of higher to lower resolutions.
+ * The first image is considered the hires "original".
+ *
* @author casties
*/
public class ImageSet {
@@ -101,11 +104,10 @@
* @return
*/
public ImageInput getNextSmaller(ImageSize size) {
- for (ListIterator i = getHiresIterator(); i.hasNext();) {
- ImageInput f = i.next();
- ImageSize is = f.getSize();
+ for (ImageInput i : list) {
+ ImageSize is = i.getSize();
if (is != null && is.isTotallySmallerThan(size)) {
- return f;
+ return i;
}
}
return null;
diff -r bb8296934cb2 -r e7d94cfbec0b doc/src/site/markdown/iiif-api.md
--- a/doc/src/site/markdown/iiif-api.md Tue Nov 10 14:41:27 2015 +0100
+++ b/doc/src/site/markdown/iiif-api.md Fri Nov 13 13:09:08 2015 +0100
@@ -2,23 +2,23 @@
The Scaler servlet provides not only its native [Scaler API](scaler-api.html) but also an API compliant to the standards of the International Image Interoperability Framework http://iiif.io.
-As of version 2.3 digilib supports the [IIIF Image API version 1.1](http://iiif.io/api/image/1.1/) at [compliance level 2](http://iiif.io/api/image/1.1/compliance.html) (since V2.3.3 even for forced w,h sizes where the image will be distorted).
+As of version 2.3.6 digilib supports the [IIIF Image API version 2](http://iiif.io/api/image/2.0/) at [compliance level 2](http://iiif.io/api/image/2.0/compliance.html) (except bitonal quality). You can switch between API version 1.1 and 2.0 support with the `iiif-api-version` parameter in [digilib-config](digilib-config.html),
IIIF Image API URLs for an image request have the form:
- http[s]://server/digilib-webapp/Scaler/iiif-prefix/identifier/region/size/rotation/quality[.format]
+ http[s]://{server}/{digilib-webapp}/Scaler/{iiif-prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
where `digilib-webapp` is the name of the digilib web application in the servlet container.
-The value of `iiif-prefix` is defined by the `iiif-prefix` parameter in the [digilib-config](digilib-config.html). The default value is "IIIF".
+The value of `iiif-prefix` is defined by the `iiif-prefix` parameter in [digilib-config](digilib-config.html). The default value is "IIIF".
The `identifier` part of the URL must not contain slashes. Since the identifier is mapped to the digilib fn-parameter, which is a filesystem path that likely contains slashes separating subdirectories, all occurrences of a slash have to be replaced by the value of the `iiif-slash-replacement` parameter in [digilib-config](digilib-config.html). The default value of the replacement string is "!", so the fn-path "books/book1/page0002" becomes the identifier "books!book1!page0002".
-For a definition of the other request parameters `region`, `size`, `rotation`, `quality`, and `format` please see the [IIIF Image API docs](http://iiif.io/api/image/1.1/).
+For a definition of the other parameters `region`, `size`, `rotation`, `quality`, and `format` please see the [IIIF Image API docs](http://iiif.io/api/image/2.0/).
A IIIF Image API image request URL could look like:
- http://www.example.org/digilib/Scaler/IIIF/books!book1!page0002/full/!150,75/0/native.jpg
+ http://www.example.org/digilib/Scaler/IIIF/books!book1!page0002/full/!150,75/0/default.jpg
An info request URL for the same image looks like:
diff -r bb8296934cb2 -r e7d94cfbec0b pdf/src/main/java/digilib/conf/PDFRequest.java
--- a/pdf/src/main/java/digilib/conf/PDFRequest.java Tue Nov 10 14:41:27 2015 +0100
+++ b/pdf/src/main/java/digilib/conf/PDFRequest.java Fri Nov 13 13:09:08 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);
}
diff -r bb8296934cb2 -r e7d94cfbec0b pdf/src/main/java/digilib/pdf/PDFStreamWorker.java
--- a/pdf/src/main/java/digilib/pdf/PDFStreamWorker.java Tue Nov 10 14:41:27 2015 +0100
+++ b/pdf/src/main/java/digilib/pdf/PDFStreamWorker.java Fri Nov 13 13:09:08 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;
}
diff -r bb8296934cb2 -r e7d94cfbec0b pdf/src/main/java/digilib/pdf/PDFTitlePage.java
--- a/pdf/src/main/java/digilib/pdf/PDFTitlePage.java Tue Nov 10 14:41:27 2015 +0100
+++ b/pdf/src/main/java/digilib/pdf/PDFTitlePage.java Fri Nov 13 13:09:08 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
diff -r bb8296934cb2 -r e7d94cfbec0b servlet/src/main/java/digilib/servlet/ServletOps.java
--- a/servlet/src/main/java/digilib/servlet/ServletOps.java Tue Nov 10 14:41:27 2015 +0100
+++ b/servlet/src/main/java/digilib/servlet/ServletOps.java Fri Nov 13 13:09:08 2015 +0100
@@ -47,6 +47,7 @@
import digilib.io.FileOpException;
import digilib.io.FileOps;
import digilib.io.ImageInput;
+import digilib.io.ImageSet;
import digilib.util.ImageSize;
public class ServletOps {
@@ -379,11 +380,16 @@
logger.error("No response!");
return;
}
+
+ /*
+ * get image size
+ */
ImageSize size = null;
+ ImageSet imageSet = null;
try {
// get original image size
- ImageInput img;
- img = dlReq.getJobDescription().getImageSet().getBiggest();
+ imageSet = dlReq.getJobDescription().getImageSet();
+ ImageInput img = imageSet.getBiggest();
size = img.getSize();
} catch (FileOpException e) {
try {
@@ -393,26 +399,83 @@
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("{");
- writer.println("\"@context\" : \"http://library.stanford.edu/iiif/image-api/1.1/context.json\",");
- writer.println("\"@id\" : \"" + url + "\",");
- writer.println("\"width\" : " + size.width + ",");
- writer.println("\"height\" : " + size.height + ",");
- writer.println("\"formats\" : [\"jpg\", \"png\"],");
- writer.println("\"qualities\" : [\"native\", \"color\", \"grey\"],");
- writer.println("\"profile\" : \"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level2\"");
- writer.println("}");
+ PrintWriter writer;
+ if (dlReq.getDigilibConfig().getAsString("iiif-api-version").startsWith("2.")) {
+ /*
+ * IIIF Image API version 2 image information
+ */
+ // use JSON-LD content type only when asked
+ String accept = dlReq.getServletRequest().getHeader("Accept");
+ if (accept != null && accept.contains("application/ld+json")) {
+ response.setContentType("application/ld+json");
+ } else {
+ response.setContentType("application/json");
+ response.setHeader("Link", ""
+ +"; rel=\"http://www.w3.org/ns/json-ld#context\""
+ +"; type=\"application/ld+json\"");
+ }
+ // write info.json
+ writer = response.getWriter();
+ writer.println("{");
+ writer.println("\"@context\" : \"http://iiif.io/api/image/2/context.json\",");
+ writer.println("\"@id\" : \"" + url + "\",");
+ writer.println("\"@protocol\" : \"http://iiif.io/api/image\",");
+ writer.println("\"width\" : " + size.width + ",");
+ writer.println("\"height\" : " + size.height + ",");
+ writer.println("\"profile\" : [");
+ writer.println(" \"http://iiif.io/api/image/2/level2.json\",");
+ writer.println(" {");
+ writer.println(" \"formats\" : [\"jpg\", \"png\"],");
+ writer.println(" \"qualities\" : [\"color\", \"gray\"],");
+ writer.println(" \"supports\" : [\"mirroring\", \"rotationArbitrary\", \"sizeAboveFull\"]");
+ writer.println(" }]");
+ // add sizes of prescaled images
+ int numImgs = imageSet.size();
+ if (numImgs > 1) {
+ writer.println(", \"sizes\" : [");
+ for (int i = numImgs - 1; i > 0; --i) {
+ ImageInput ii = imageSet.get(i);
+ ImageSize is = ii.getSize();
+ writer.println(" {\"width\" : "+is.getWidth()+", \"height\" : "+is.getHeight()+"}"
+ +((i > 1)?",":""));
+ }
+ writer.println("]");
+ }
+ writer.println("}");
+
+ } else {
+ /*
+ * IIIF Image API version 1 image information
+ */
+ response.setContentType("application/json,application/ld+json");
+ writer = response.getWriter();
+ writer.println("{");
+ writer.println("\"@context\" : \"http://library.stanford.edu/iiif/image-api/1.1/context.json\",");
+ writer.println("\"@id\" : \"" + url + "\",");
+ writer.println("\"width\" : " + size.width + ",");
+ writer.println("\"height\" : " + size.height + ",");
+ writer.println("\"formats\" : [\"jpg\", \"png\"],");
+ writer.println("\"qualities\" : [\"native\", \"color\", \"grey\"],");
+ writer.println("\"profile\" : \"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level2\"");
+ writer.println("}");
+ }
} catch (IOException e) {
throw new ServletException("Unable to write response!", e);
}
diff -r bb8296934cb2 -r e7d94cfbec0b servlet2/src/main/java/digilib/servlet/Scaler.java
--- a/servlet2/src/main/java/digilib/servlet/Scaler.java Tue Nov 10 14:41:27 2015 +0100
+++ b/servlet2/src/main/java/digilib/servlet/Scaler.java Fri Nov 13 13:09:08 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) {
diff -r bb8296934cb2 -r e7d94cfbec0b servlet2/src/main/java/digilib/servlet/ScalerNoThread.java
--- a/servlet2/src/main/java/digilib/servlet/ScalerNoThread.java Tue Nov 10 14:41:27 2015 +0100
+++ b/servlet2/src/main/java/digilib/servlet/ScalerNoThread.java Fri Nov 13 13:09:08 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
*/
diff -r bb8296934cb2 -r e7d94cfbec0b servlet3/src/main/java/digilib/servlet/Scaler.java
--- a/servlet3/src/main/java/digilib/servlet/Scaler.java Tue Nov 10 14:41:27 2015 +0100
+++ b/servlet3/src/main/java/digilib/servlet/Scaler.java Fri Nov 13 13:09:08 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,26 @@
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 IIIF info-request
+ if (dlRequest.hasOption("info")) {
+ ServletOps.sendIiifInfo(dlRequest, response, logger);
+ return;
+ }
+ if (dlRequest.hasOption("redirect-info")) {
+ // TODO: the redirect should have code 303
+ response.sendRedirect("info.json");
+ return;
+ }
+
+ // error out if request was bad
+ if (dlRequest.errorMessage != null) {
+ digilibError(errMsgType, Error.UNKNOWN, dlRequest.errorMessage, response);
+ return;
+ }
/*
* check permissions
@@ -280,6 +281,11 @@
}
/*
+ * get the input file
+ */
+ ImageInput fileToLoad = jobTicket.getInput();
+
+ /*
* if requested, send image as a file
*/
if (sendFileAllowed && jobTicket.getSendAsFile()) {
diff -r bb8296934cb2 -r e7d94cfbec0b webapp/pom.xml
--- a/webapp/pom.xml Tue Nov 10 14:41:27 2015 +0100
+++ b/webapp/pom.xml Fri Nov 13 13:09:08 2015 +0100
@@ -11,6 +11,10 @@
The Digital Image Library - web application server and HTML and JS clients.
http://digilib.sourceforge.net
war
+
+
+ true
+
@@ -31,7 +35,15 @@
-
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.19
+
+ ${skipTests}
+
+
+
@@ -157,5 +169,39 @@
+
+ cors-filter
+
+ true
+
+
+
+
+ org.eclipse.jetty
+ jetty-servlets
+ 9.2.13.v20150730
+
+
+
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ 9.2.13.v20150730
+ test
+
+
+ org.eclipse.jetty
+ jetty-http
+ 9.2.13.v20150730
+ test
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
diff -r bb8296934cb2 -r e7d94cfbec0b webapp/src/main/webapp/WEB-INF/web-3.0.xml
--- a/webapp/src/main/webapp/WEB-INF/web-3.0.xml Tue Nov 10 14:41:27 2015 +0100
+++ b/webapp/src/main/webapp/WEB-INF/web-3.0.xml Fri Nov 13 13:09:08 2015 +0100
@@ -50,4 +50,18 @@
/Scaler/*
+
+
+
+ CORS
+
+
+ org.eclipse.jetty.servlets.CrossOriginFilter
+ true
+
+
+ CORS
+ /*
+
+
diff -r bb8296934cb2 -r e7d94cfbec0b webapp/src/main/webapp/WEB-INF/web-additional.xml
--- a/webapp/src/main/webapp/WEB-INF/web-additional.xml Tue Nov 10 14:41:27 2015 +0100
+++ b/webapp/src/main/webapp/WEB-INF/web-additional.xml Fri Nov 13 13:09:08 2015 +0100
@@ -75,4 +75,17 @@
+
+
+ CORS
+
+
+ org.eclipse.jetty.servlets.CrossOriginFilter
+ true
+
+
+ CORS
+ /*
+
+
diff -r bb8296934cb2 -r e7d94cfbec0b webapp/src/main/webapp/jquery/jquery.digilib.js
diff -r bb8296934cb2 -r e7d94cfbec0b webapp/src/test/java/digilib/servlet/ScalerTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webapp/src/test/java/digilib/servlet/ScalerTest.java Fri Nov 13 13:09:08 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
+ * .
+ * #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);
+ }
+
+
+}