summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--htroot/ViewFavicon.java198
-rw-r--r--htroot/ViewImage.java537
-rw-r--r--htroot/yacysearchitem.java11
-rw-r--r--source/net/yacy/kelondro/data/meta/URIMetadataNode.java2
-rw-r--r--source/net/yacy/visualization/ImageViewer.java491
-rwxr-xr-xtest/ImageViewerPerfTest.java (renamed from test/ViewImagePerfTest.java)61
-rwxr-xr-xtest/ImageViewerTest.java (renamed from test/ViewImageTest.java)67
-rw-r--r--test/java/yacysearchitemTest.java20
8 files changed, 821 insertions, 566 deletions
diff --git a/htroot/ViewFavicon.java b/htroot/ViewFavicon.java
new file mode 100644
index 000000000..68c45125b
--- /dev/null
+++ b/htroot/ViewFavicon.java
@@ -0,0 +1,198 @@
+
+// ViewFavicon.java
+// -----------------------
+// part of YaCy
+// (C) by Michael Peter Christen; mc@yacy.net
+// first published on http://www.anomic.de
+// Frankfurt, Germany, 2006
+// created 03.04.2006
+//
+// 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.
+//
+// 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 Public License for more details.
+//
+// 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
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageInputStream;
+
+import net.yacy.cora.document.id.DigestURL;
+import net.yacy.cora.document.id.MultiProtocolURL;
+import net.yacy.cora.protocol.Domains;
+import net.yacy.cora.protocol.HeaderFramework;
+import net.yacy.cora.protocol.RequestHeader;
+import net.yacy.cora.storage.ConcurrentARC;
+import net.yacy.cora.util.ConcurrentLog;
+import net.yacy.kelondro.util.FileUtils;
+import net.yacy.kelondro.util.MemoryControl;
+import net.yacy.kelondro.workflow.WorkflowProcessor;
+import net.yacy.peers.graphics.EncodedImage;
+import net.yacy.search.Switchboard;
+import net.yacy.server.serverObjects;
+import net.yacy.server.serverSwitch;
+import net.yacy.visualization.ImageViewer;
+
+/**
+ * Extends ViewImage behavior : add a specific favicon cache and use of a
+ * default image on loading error.
+ *
+ * @author luc
+ *
+ */
+public class ViewFavicon {
+
+ /** Single instance of ImageViewer */
+ private static final ImageViewer VIEWER = new ImageViewer();
+
+ /** Icons cache encoded as png */
+ private static Map<String, byte[]> pngIconCache = new ConcurrentARC<String, byte[]>(1000,
+ Math.max(10, Math.min(32, WorkflowProcessor.availableCPU * 2)));
+
+ /** Default icon local file */
+ private static final String defaulticon = "htroot/env/grafics/dfltfvcn.ico";
+
+ /**
+ * Default icon encoded as png : we use a bvte array as it is thread-safe
+ * instead of a ByteBuffer in EncodedImage
+ */
+ private static byte[] defaultPNGEncodedIcon = null;
+
+ /**
+ * Try parsing image from post "url" parameter (authenticated users) or from
+ * "code" parameter (non authenticated users). When image could be parsed,
+ * try encoding to target format specified by header "EXT". When any error
+ * occurs, return default icon.
+ *
+ * @param header
+ * request header
+ * @param post
+ * post parameters
+ * @param env
+ * Switchboard instance
+ * @return an {@link EncodedImage} instance encoded in format specified in
+ * post, or an InputStream pointing to original image data.
+ */
+ public static Object respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
+
+ final Switchboard sb = (Switchboard) env;
+ String ext = header.get("EXT", null);
+ boolean isPNGTarget = "png".equalsIgnoreCase(ext);
+
+ ImageInputStream imageInStream = null;
+ InputStream inStream = null;
+ byte[] resultBytes = null;
+ try {
+ /* Clear icon cache when running out of memory */
+ if (MemoryControl.shortStatus()) {
+ pngIconCache.clear();
+ }
+
+ final boolean auth = Domains.isLocalhost(header.get(HeaderFramework.CONNECTION_PROP_CLIENTIP, ""))
+ || sb.verifyAuthentication(header); // handle access rights
+
+ DigestURL url = VIEWER.parseURL(post, auth);
+
+ final String normalizedURL = url.toNormalform(false);
+
+ if (isPNGTarget) {
+ resultBytes = pngIconCache.get(normalizedURL);
+ }
+ /* Icon is not already in cache */
+ if (resultBytes == null) {
+ String urlExt = MultiProtocolURL.getFileExtension(url.getFileName());
+ if (ext != null && ext.equalsIgnoreCase(urlExt) && ImageViewer.isBrowserRendered(urlExt)) {
+ return VIEWER.openInputStream(post, sb.loader, auth, url);
+ }
+ /*
+ * When opening a file, the most efficient is to open
+ * ImageInputStream directly on file
+ */
+ if (url.isFile()) {
+ imageInStream = ImageIO.createImageInputStream(url.getFSFile());
+ } else {
+ inStream = VIEWER.openInputStream(post, sb.loader, auth, url);
+ imageInStream = ImageIO.createImageInputStream(inStream);
+ }
+ // read image
+ EncodedImage encodedIcon = VIEWER.parseAndScale(post, auth, url, ext, imageInStream);
+
+ if (encodedIcon != null && !encodedIcon.getImage().isEmpty()) {
+ resultBytes = encodedIcon.getImage().getBytes();
+ if (isPNGTarget && encodedIcon.getImage().length() <= 10240) {
+ /* Only store in cache icon images below 10KB, png encoded */
+ pngIconCache.put(normalizedURL, resultBytes);
+ }
+ }
+ }
+ } catch (IOException e) {
+ ConcurrentLog.fine("ViewFavicon", "Error loading favicon, default one wille be used : " + e);
+ } finally {
+ /*
+ * imageInStream.close() method doesn't close source input stream
+ */
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException ignored) {
+ }
+ }
+ if (resultBytes == null) {
+ /*
+ * I any error occured when loading icon, return default one
+ */
+ if (ext == null || isPNGTarget) {
+ /* Load default icon only once */
+ if (defaultPNGEncodedIcon == null) {
+ defaultPNGEncodedIcon = loadDefaultIcon(post, sb, ext);
+ }
+ resultBytes = defaultPNGEncodedIcon;
+ } else {
+ resultBytes = loadDefaultIcon(post, sb, ext);
+ }
+ }
+
+ }
+
+ return new ByteArrayInputStream(resultBytes);
+ }
+
+ /**
+ * Load default icon and encode it to ext format
+ *
+ * @param post
+ * post parameters
+ * @param sb
+ * Switchboard instance
+ * @param ext
+ * target image format
+ * @return icon encoded bytes, empty if and exception occured when loading
+ * or rendering
+ */
+ private static byte[] loadDefaultIcon(final serverObjects post, final Switchboard sb, String ext) {
+ byte[] resultBytes;
+ byte[] defaultBytes = new byte[0];
+ try {
+ defaultBytes = FileUtils.read(new File(sb.getAppPath(), defaulticon));
+ } catch (final IOException initicon) {
+ defaultBytes = new byte[0];
+ } finally {
+ resultBytes = new EncodedImage(defaultBytes, ext, post.getBoolean("isStatic")).getImage().getBytes();
+ }
+ return resultBytes;
+ }
+
+}
diff --git a/htroot/ViewImage.java b/htroot/ViewImage.java
index 45dd15c31..4625e9367 100644
--- a/htroot/ViewImage.java
+++ b/htroot/ViewImage.java
@@ -21,54 +21,28 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.MediaTracker;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-import java.awt.image.Raster;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Iterator;
-import java.util.Map;
import javax.imageio.ImageIO;
-import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import net.yacy.cora.document.id.DigestURL;
import net.yacy.cora.document.id.MultiProtocolURL;
-import net.yacy.cora.federate.yacy.CacheStrategy;
-import net.yacy.cora.protocol.ClientIdentification;
import net.yacy.cora.protocol.Domains;
import net.yacy.cora.protocol.HeaderFramework;
import net.yacy.cora.protocol.RequestHeader;
-import net.yacy.cora.storage.ConcurrentARC;
-import net.yacy.cora.util.ConcurrentLog;
-import net.yacy.data.InvalidURLLicenceException;
-import net.yacy.data.URLLicense;
import net.yacy.http.servlets.TemplateMissingParameterException;
-import net.yacy.kelondro.util.FileUtils;
-import net.yacy.kelondro.util.MemoryControl;
-import net.yacy.kelondro.workflow.WorkflowProcessor;
import net.yacy.peers.graphics.EncodedImage;
-import net.yacy.repository.Blacklist.BlacklistType;
-import net.yacy.repository.LoaderDispatcher;
import net.yacy.search.Switchboard;
import net.yacy.server.serverObjects;
import net.yacy.server.serverSwitch;
+import net.yacy.visualization.ImageViewer;
public class ViewImage {
-
- private static Map<String, Image> iconcache = new ConcurrentARC<String, Image>(1000,
- Math.max(10, Math.min(32, WorkflowProcessor.availableCPU * 2)));
-
- private static String defaulticon = "htroot/env/grafics/dfltfvcn.ico";
- private static byte[] defaulticonb = null;
+
+ /** Single instance of ImageViewer */
+ private static final ImageViewer VIEWER = new ImageViewer();
/**
* Try parsing image from post "url" parameter (authenticated users) or from "code" parameter (non authenticated users).
@@ -91,6 +65,7 @@ public class ViewImage {
* Sould end in a HTTP 500 error whose processing is more
* consistent across browsers than a response with zero content
* bytes.
+ * @throws TemplateMissingParameterException when one required parameter is missing
*/
public static Object respond(final RequestHeader header, final serverObjects post, final serverSwitch env)
throws IOException {
@@ -100,501 +75,59 @@ public class ViewImage {
if(post == null) {
throw new TemplateMissingParameterException("please fill at least url or code parameter");
}
- String urlString = post.get("url", "");
- final String urlLicense = post.get("code", "");
+
String ext = header.get("EXT", null);
final boolean auth = Domains.isLocalhost(header.get(HeaderFramework.CONNECTION_PROP_CLIENTIP, ""))
|| sb.verifyAuthentication(header); // handle access rights
- DigestURL url = null;
- if(auth) {
- /* Authenticated user : rely on url parameter*/
- if (urlString.length() > 0) {
- url = new DigestURL(urlString);
- } else {
- throw new TemplateMissingParameterException("missing required url parameter");
- }
- } else {
- /* Non authenticated user : rely on urlLicense parameter */
- if((urlLicense.length() > 0)) {
- urlString = URLLicense.releaseLicense(urlLicense);
- if (urlString != null) {
- url = new DigestURL(urlString);
- } else { // license is gone (e.g. released/remove in prev calls)
- ConcurrentLog.fine("ViewImage", "image urlLicense not found key=" + urlLicense);
- /* Caller is responsible for handling this with appropriate HTTP status code */
- throw new InvalidURLLicenceException();
- }
- } else {
- throw new TemplateMissingParameterException("missing required code parameter");
- }
- }
+ DigestURL url = VIEWER.parseURL(post, auth);
// get the image as stream
- if (MemoryControl.shortStatus()) {
- iconcache.clear();
- }
- EncodedImage encodedImage = null;
- Image image = iconcache.get(urlString);
- if (image != null) {
- encodedImage = new EncodedImage(image, ext, post.getBoolean("isStatic"));
- } else {
-
- ImageInputStream imageInStream = null;
- InputStream inStream = null;
- try {
- String urlExt = MultiProtocolURL.getFileExtension(url.getFileName());
- if (ext != null && ext.equalsIgnoreCase(urlExt) && isBrowserRendered(urlExt)) {
- return openInputStream(post, sb.loader, auth, url);
- }
- /*
- * When opening a file, the most efficient is to open
- * ImageInputStream directly on file
- */
- if (url.isFile()) {
- imageInStream = ImageIO.createImageInputStream(url.getFSFile());
- } else {
- inStream = openInputStream(post, sb.loader, auth, url);
- imageInStream = ImageIO.createImageInputStream(inStream);
- }
- // read image
- encodedImage = parseAndScale(post, auth, urlString, ext, imageInStream);
- } catch(Exception e) {
- /* Exceptions are not propagated here : many error causes are possible, network errors,
- * incorrect or unsupported format, bad ImageIO plugin...
- * Instead return an empty EncodedImage. Caller is responsible for handling this correctly (500 status code response) */
-
- if ("favicon.ico".equalsIgnoreCase(url.getFileName())) { // but on missing favicon just present a default (occures frequently by call from searchitem.html)
- // currently yacysearchitem assigns "hosturl/favicon.ico" (to look for the filename should not much interfere with other situatios)
- if (defaulticonb == null) { // load the default icon once
- try {
- defaulticonb = FileUtils.read(new File(sb.getAppPath(), defaulticon));
- } catch (final IOException initicon) {
- defaulticonb = new byte[0];
- }
- }
- encodedImage = new EncodedImage(defaulticonb, ext, post.getBoolean("isStatic"));
- } else {
- encodedImage = new EncodedImage(new byte[0], ext, post.getBoolean("isStatic"));
- }
- } finally {
- /*
- * imageInStream.close() method doesn't close source input
- * stream
- */
- if (inStream != null) {
- try {
- inStream.close();
- } catch (IOException ignored) {
- }
- }
- }
- }
-
- return encodedImage;
- }
-
- /**
- * Open input stream on image url using provided loader. All parameters must
- * not be null.
- *
- * @param post
- * post parameters.
- * @param loader.
- * Resources loader.
- * @param auth
- * true when user has credentials to load full images.
- * @param url
- * image url.
- * @return an open input stream instance (don't forget to close it).
- * @throws IOException
- * when a read/write error occured.
- */
- private static InputStream openInputStream(final serverObjects post, final LoaderDispatcher loader,
- final boolean auth, DigestURL url) throws IOException {
- InputStream inStream = null;
- if (url != null) {
- try {
- String agentName = post.get("agentName", auth ? ClientIdentification.yacyIntranetCrawlerAgentName
- : ClientIdentification.yacyInternetCrawlerAgentName);
- ClientIdentification.Agent agent = ClientIdentification.getAgent(agentName);
- inStream = loader.openInputStream(loader.request(url, false, true), CacheStrategy.IFEXIST,
- BlacklistType.SEARCH, agent);
- } catch (final IOException e) {
- /** No need to log full stack trace (in most cases resource is not available because of a network error) */
- ConcurrentLog.fine("ViewImage", "cannot load image. URL : " + url.toNormalform(true));
- throw e;
- }
- }
- if (inStream == null) {
- throw new IOException("Input stream could no be open");
- }
- return inStream;
- }
-
- /**
- * @param formatName
- * informal file format name. For example : "png".
- * @return true when image format is rendered by browser and not by
- * ViewImage internals
- */
- public static boolean isBrowserRendered(String formatName) {
- /*
- * gif images are not loaded because of an animated gif bug within jvm
- * which sends java into an endless loop with high CPU
- */
- /*
- * svg images not supported by jdk, but by most browser, deliver just
- * content (without crop/scale)
- */
- return ("gif".equalsIgnoreCase(formatName) || "svg".equalsIgnoreCase(formatName));
- }
-
- /**
- * Process source image to try to produce an EncodedImage instance
- * eventually scaled and clipped depending on post parameters. When
- * processed, imageInStream is closed.
- *
- * @param post
- * request post parameters. Must not be null.
- * @param auth
- * true when access rigths are OK.
- * @param urlString
- * image source URL as String. Must not be null.
- * @param ext
- * target image file format. May be null.
- * @param imageInStream
- * open stream on image content. Must not be null.
- * @return an EncodedImage instance.
- * @throws IOException
- * when image could not be parsed or encoded to specified format.
- */
- protected static EncodedImage parseAndScale(serverObjects post, boolean auth, String urlString, String ext,
- ImageInputStream imageInStream) throws IOException {
EncodedImage encodedImage;
- // BufferedImage image = ImageIO.read(imageInStream);
- Iterator<ImageReader> readers = ImageIO.getImageReaders(imageInStream);
- if (!readers.hasNext()) {
- try {
- /* When no reader can be found, we have to close the stream */
- imageInStream.close();
- } catch (IOException ignoredException) {
+ ImageInputStream imageInStream = null;
+ InputStream inStream = null;
+ try {
+ String urlExt = MultiProtocolURL.getFileExtension(url.getFileName());
+ if (ext != null && ext.equalsIgnoreCase(urlExt) && ImageViewer.isBrowserRendered(urlExt)) {
+ return VIEWER.openInputStream(post, sb.loader, auth, url);
}
- String errorMessage = "Image format (" + ext + ") is not supported.";
- ConcurrentLog.fine("ViewImage", errorMessage + "Image URL : " + urlString);
/*
- * Throw an exception, wich will end in a HTTP 500 response, better
- * handled by browsers than an empty image
+ * When opening a file, the most efficient is to open
+ * ImageInputStream directly on file
*/
- throw new IOException(errorMessage);
- }
- ImageReader reader = readers.next();
- reader.setInput(imageInStream, true, true);
-
- int maxwidth = post.getInt("maxwidth", 0);
- int maxheight = post.getInt("maxheight", 0);
- final boolean quadratic = post.containsKey("quadratic");
- boolean isStatic = post.getBoolean("isStatic");
- BufferedImage image = null;
- boolean returnRaw = true;
- if (!auth || maxwidth != 0 || maxheight != 0) {
-
- // find original size
- final int originWidth = reader.getWidth(0);
- final int originHeigth = reader.getHeight(0);
-
- // in case of not-authorized access shrink the image to
- // prevent
- // copyright problems, so that images are not larger than
- // thumbnails
- Dimension maxDimensions = calculateMaxDimensions(auth, originWidth, originHeigth, maxwidth, maxheight);
-
- // if a quadratic flag is set, we cut the image out to be in
- // quadratic shape
- int w = originWidth;
- int h = originHeigth;
- if (quadratic && originWidth != originHeigth) {
- Rectangle square = getMaxSquare(originHeigth, originWidth);
- h = square.height;
- w = square.width;
- }
-
- Dimension finalDimensions = calculateDimensions(w, h, maxDimensions);
-
- if (originWidth != finalDimensions.width || originHeigth != finalDimensions.height) {
- returnRaw = false;
- image = readImage(reader);
- if (quadratic && originWidth != originHeigth) {
- image = makeSquare(image);
- }
- image = scale(finalDimensions.width, finalDimensions.height, image);
- }
- if (finalDimensions.width == 16 && finalDimensions.height == 16) {
- // this might be a favicon, store image to cache for
- // faster
- // re-load later on
- if (image == null) {
- returnRaw = false;
- image = readImage(reader);
- }
- iconcache.put(urlString, image);
- }
- }
- /* Image do not need to be scaled or cropped */
- if (returnRaw) {
- if (!reader.getFormatName().equalsIgnoreCase(ext) || imageInStream.getFlushedPosition() != 0) {
- /*
- * image parsing and reencoding is only needed when source image
- * and target formats differ, or when first bytes have been discarded
- */
- returnRaw = false;
- image = readImage(reader);
+ if (url.isFile()) {
+ imageInStream = ImageIO.createImageInputStream(url.getFSFile());
+ } else {
+ inStream = VIEWER.openInputStream(post, sb.loader, auth, url);
+ imageInStream = ImageIO.createImageInputStream(inStream);
}
- }
- if (returnRaw) {
- byte[] imageData = readRawImage(imageInStream);
- encodedImage = new EncodedImage(imageData, ext, isStatic);
- } else {
+ // read image
+ encodedImage = VIEWER.parseAndScale(post, auth, url, ext, imageInStream);
+ } catch (Exception e) {
/*
- * An error can still occur when transcoding from buffered image to
- * target ext : in that case EncodedImage.getImage() is empty.
+ * Exceptions are not propagated here : many error causes are
+ * possible, network errors, incorrect or unsupported format, bad
+ * ImageIO plugin... Instead return an empty EncodedImage. Caller is
+ * responsible for handling this correctly (500 status code
+ * response)
*/
- encodedImage = new EncodedImage(image, ext, isStatic);
- if (encodedImage.getImage().length() == 0) {
- String errorMessage = "Image could not be encoded to format : " + ext;
- ConcurrentLog.fine("ViewImage", errorMessage + ". Image URL : " + urlString);
- throw new IOException(errorMessage);
- }
- }
-
- return encodedImage;
- }
-
- /**
- * Read image using specified reader and close ImageInputStream source.
- * Input must have bean set before using
- * {@link ImageReader#setInput(Object)}
- *
- * @param reader
- * image reader. Must not be null.
- * @return buffered image
- * @throws IOException
- * when an error occured
- */
- private static BufferedImage readImage(ImageReader reader) throws IOException {
- BufferedImage image;
- try {
- image = reader.read(0);
+ encodedImage = new EncodedImage(new byte[0], ext, post.getBoolean("isStatic"));
} finally {
- reader.dispose();
- Object input = reader.getInput();
- if (input instanceof ImageInputStream) {
- try {
- ((ImageInputStream) input).close();
- } catch (IOException ignoredException) {
- }
- }
- }
- return image;
- }
-
- /**
- * Read image data without parsing.
- *
- * @param inStream
- * image source. Must not be null. First bytes must not have been marked discarded ({@link ImageInputStream#getFlushedPosition()} must be zero)
- * @return image data as bytes
- * @throws IOException
- * when a read/write error occured.
- */
- private static byte[] readRawImage(ImageInputStream inStream) throws IOException {
- byte[] buffer = new byte[4096];
- int l = 0;
- ByteArrayOutputStream outStream = new ByteArrayOutputStream();
- inStream.seek(0);
- try {
- while ((l = inStream.read(buffer)) >= 0) {
- outStream.write(buffer, 0, l);
- }
- return outStream.toByteArray();
- } finally {
- try {
- inStream.close();
- } catch (IOException ignored) {
- }
- }
- }
-
- /**
- * Calculate image dimensions from image original dimensions, max
- * dimensions, and target dimensions.
- *
- * @return dimensions to render image
- */
- protected static Dimension calculateDimensions(final int originWidth, final int originHeight, final Dimension max) {
- int resultWidth;
- int resultHeight;
- if (max.width < originWidth || max.height < originHeight) {
- // scale image
- final double hs = (originWidth <= max.width) ? 1.0 : ((double) max.width) / ((double) originWidth);
- final double vs = (originHeight <= max.height) ? 1.0 : ((double) max.height) / ((double) originHeight);
- final double scale = Math.min(hs, vs);
- // if (!auth) scale = Math.min(scale, 0.6); // this is for copyright
- // purpose
- if (scale < 1.0) {
- resultWidth = Math.max(1, (int) (originWidth * scale));
- resultHeight = Math.max(1, (int) (originHeight * scale));
- } else {
- resultWidth = Math.max(1, originWidth);
- resultHeight = Math.max(1, originHeight);
- }
-
- } else {
- // do not scale
- resultWidth = originWidth;
- resultHeight = originHeight;
- }
- return new Dimension(resultWidth, resultHeight);
- }
-
- /**
- * Calculate image maximum dimentions from original and specified maximum
- * dimensions
- *
- * @param auth
- * true when acces rigths are OK.
- * @return maximum dimensions to render image
- */
- protected static Dimension calculateMaxDimensions(final boolean auth, final int originWidth, final int originHeight,
- final int maxWidth, final int maxHeight) {
- int resultWidth;
- int resultHeight;
- // in case of not-authorized access shrink the image to prevent
- // copyright problems, so that images are not larger than thumbnails
- if (auth) {
- resultWidth = (maxWidth == 0) ? originWidth : maxWidth;
- resultHeight = (maxHeight == 0) ? originHeight : maxHeight;
- } else if ((originWidth > 16) || (originHeight > 16)) {
- resultWidth = Math.min(96, originWidth);
- resultHeight = Math.min(96, originHeight);
- } else {
- resultWidth = 16;
- resultHeight = 16;
- }
- return new Dimension(resultWidth, resultHeight);
- }
-
- /**
- * Scale image to specified dimensions
- *
- * @param width
- * target width
- * @param height
- * target height
- * @param image
- * image to scale. Must not be null.
- * @return a scaled image
- */
- protected static BufferedImage scale(final int width, final int height, final BufferedImage image) {
- // compute scaled image
- Image scaled = image.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
- final MediaTracker mediaTracker = new MediaTracker(new Container());
- mediaTracker.addImage(scaled, 0);
- try {
- mediaTracker.waitForID(0);
- } catch (final InterruptedException e) {
- }
-
- // make a BufferedImage out of that
- BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- try {
- result.createGraphics().drawImage(scaled, 0, 0, width, height, null);
- // check outcome
- final Raster raster = result.getData();
- int[] pixel = new int[raster.getSampleModel().getNumBands()];
- pixel = raster.getPixel(0, 0, pixel);
- } catch (final Exception e) {
/*
- * Exception may be caused by source image color model : try now to
- * convert to RGB before scaling
+ * imageInStream.close() method doesn't close source input stream
*/
- try {
- BufferedImage converted = EncodedImage.convertToRGB(image);
- scaled = converted.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
- mediaTracker.addImage(scaled, 1);
+ if (inStream != null) {
try {
- mediaTracker.waitForID(1);
- } catch (final InterruptedException e2) {
+ inStream.close();
+ } catch (IOException ignored) {
}
- result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- result.createGraphics().drawImage(scaled, 0, 0, width, height, null);
-
- // check outcome
- final Raster raster = result.getData();
- int[] pixel = new int[result.getSampleModel().getNumBands()];
- pixel = raster.getPixel(0, 0, pixel);
- } catch (Exception e2) {
- result = image;
}
-
- ConcurrentLog.fine("ViewImage", "Image could not be scaled");
}
- return result;
- }
- /**
- *
- * @param h
- * image height
- * @param w
- * image width
- * @return max square area fitting inside dimensions
- */
- protected static Rectangle getMaxSquare(final int h, final int w) {
- Rectangle square;
- if (w > h) {
- final int offset = (w - h) / 2;
- square = new Rectangle(offset, 0, h, h);
- } else {
- final int offset = (h - w) / 2;
- square = new Rectangle(0, offset, w, w);
- }
- return square;
+ return encodedImage;
}
- /**
- * Crop image to make a square
- *
- * @param image
- * image to crop
- * @return
- */
- protected static BufferedImage makeSquare(BufferedImage image) {
- final int w = image.getWidth();
- final int h = image.getHeight();
- if (w > h) {
- final BufferedImage dst = new BufferedImage(h, h, BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = dst.createGraphics();
- final int offset = (w - h) / 2;
- try {
- g.drawImage(image, 0, 0, h - 1, h - 1, offset, 0, h + offset, h - 1, null);
- } finally {
- g.dispose();
- }
- image = dst;
- } else {
- final BufferedImage dst = new BufferedImage(w, w, BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = dst.createGraphics();
- final int offset = (h - w) / 2;
- try {
- g.drawImage(image, 0, 0, w - 1, w - 1, 0, offset, w - 1, w + offset, null);
- } finally {
- g.dispose();
- }
- image = dst;
- }
- return image;
- }
+
}
diff --git a/htroot/yacysearchitem.java b/htroot/yacysearchitem.java
index cce332601..3ba1d727b 100644
--- a/htroot/yacysearchitem.java
+++ b/htroot/yacysearchitem.java
@@ -68,6 +68,7 @@ import net.yacy.server.serverObjects;
import net.yacy.server.serverSwitch;
import net.yacy.utils.crypt;
import net.yacy.utils.nxTools;
+import net.yacy.visualization.ImageViewer;
public class yacysearchitem {
@@ -383,10 +384,14 @@ public class yacysearchitem {
* is null
*/
private static String processFaviconURL(final boolean authenticated, DigestURL faviconURL) {
+ final String iconUrlExt = MultiProtocolURL.getFileExtension(faviconURL.getFileName());
+ /* Image format ouput for ViewFavicon servlet : default is png, except with gif and svg icons */
+ final String viewFaviconExt = !iconUrlExt.isEmpty() && ImageViewer.isBrowserRendered(iconUrlExt) ? iconUrlExt : "png";
+
/* Only use licence code for non authentified users. For authenticated users licence would never be released and would unnecessarily fill URLLicense.permissions. */
StringBuilder contentFaviconURL = new StringBuilder();
if (faviconURL != null) {
- contentFaviconURL.append("ViewImage.png?width=16&height=16&isStatic=true");
+ contentFaviconURL.append("ViewFavicon.").append(viewFaviconExt).append("?maxwidth=16&maxheight=16&isStatic=true&quadratic");
if (authenticated) {
contentFaviconURL.append("&url=").append(faviconURL.toNormalform(true));
} else {
@@ -418,7 +423,7 @@ public class yacysearchitem {
final String license = URLLicense.aquireLicense(image.imageUrl); // this is just the license key to get the image forwarded through the YaCy thumbnail viewer, not an actual lawful license
/* Image format ouput for ViewImage servlet : default is png, except with gif and svg images */
- final String viewImageExt = !imageUrlExt.isEmpty() && ViewImage.isBrowserRendered(imageUrlExt) ? imageUrlExt : "png";
+ final String viewImageExt = !imageUrlExt.isEmpty() && ImageViewer.isBrowserRendered(imageUrlExt) ? imageUrlExt : "png";
/* Thumb URL */
StringBuilder thumbURLBuilder = new StringBuilder("ViewImage.").append(viewImageExt).append("?maxwidth=")
.append(DEFAULT_IMG_WIDTH).append("&maxheight=").append(DEFAULT_IMG_HEIGHT)
@@ -449,7 +454,7 @@ public class yacysearchitem {
/* When image content is rendered by browser :
* - set smaller dimension to 100% in order to crop image on other dimension with CSS style 'overflow:hidden' on image container
* - set negative margin top behave like ViewImage which sets an offset when cutting to square */
- if (ViewImage.isBrowserRendered(imageUrlExt)) {
+ if (ImageViewer.isBrowserRendered(imageUrlExt)) {
if (image.width > image.height) {
/* Landscape orientation */
itemWidth = "";
diff --git a/source/net/yacy/kelondro/data/meta/URIMetadataNode.java b/source/net/yacy/kelondro/data/meta/URIMetadataNode.java
index 785ffe62a..e0eaf56e1 100644
--- a/source/net/yacy/kelondro/data/meta/URIMetadataNode.java
+++ b/source/net/yacy/kelondro/data/meta/URIMetadataNode.java
@@ -608,7 +608,7 @@ public class URIMetadataNode extends SolrDocument /* implements Comparable<URIMe
* No standard icon yet found : prefer a standard icon, or check
* size
*/
- match = isStandard || distance < closestDistance;
+ match = isStandard || distance <= closestDistance;
}
if (match) {
faviconEntry = icon;
diff --git a/source/net/yacy/visualization/ImageViewer.java b/source/net/yacy/visualization/ImageViewer.java
new file mode 100644
index 000000000..867320277
--- /dev/null
+++ b/source/net/yacy/visualization/ImageViewer.java
@@ -0,0 +1,491 @@
+/**
+ * ImageViewer
+ * Copyright 2005 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
+ * First released 16.09.2005 at http://yacy.net
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program in the file lgpl21.txt
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.yacy.visualization;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+import net.yacy.cora.document.id.DigestURL;
+import net.yacy.cora.document.id.MultiProtocolURL;
+import net.yacy.cora.federate.yacy.CacheStrategy;
+import net.yacy.cora.protocol.ClientIdentification;
+import net.yacy.cora.util.ConcurrentLog;
+import net.yacy.data.InvalidURLLicenceException;
+import net.yacy.data.URLLicense;
+import net.yacy.http.servlets.TemplateMissingParameterException;
+import net.yacy.peers.graphics.EncodedImage;
+import net.yacy.repository.Blacklist.BlacklistType;
+import net.yacy.repository.LoaderDispatcher;
+import net.yacy.server.serverObjects;
+
+/**
+ * Provides methods for image or favicon viewing in YaCy servlets.
+ * @author luc
+ *
+ */
+public class ImageViewer {
+
+ /**
+ * Try to get image URL from parameters.
+ * @param post post parameters. Must not be null.
+ * @param auth true when current user is authenticated
+ * @return DigestURL instance
+ * @throws MalformedURLException when url is malformed
+ * @throws TemplateMissingParameterException when urlString or urlLicense is missing (the one needed depends on auth)
+ */
+ public DigestURL parseURL(final serverObjects post, final boolean auth)
+ throws MalformedURLException {
+ final String urlString = post.get("url", "");
+ final String urlLicense = post.get("code", "");
+ DigestURL url;
+ if(auth) {
+ /* Authenticated user : rely on url parameter*/
+ if (urlString.length() > 0) {
+ url = new DigestURL(urlString);
+ } else {
+ throw new TemplateMissingParameterException("missing required url parameter");
+ }
+ } else {
+ /* Non authenticated user : rely on urlLicense parameter */
+ if((urlLicense.length() > 0)) {
+ String licensedURL = URLLicense.releaseLicense(urlLicense);
+ if (licensedURL != null) {
+ url = new DigestURL(licensedURL);
+ } else { // license is gone (e.g. released/remove in prev calls)
+ ConcurrentLog.fine("ImageViewer", "image urlLicense not found key=" + urlLicense);
+ /* Caller is responsible for handling this with appropriate HTTP status code */
+ throw new InvalidURLLicenceException();
+ }
+ } else {
+ throw new TemplateMissingParameterException("missing required code parameter");
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Open input stream on image url using provided loader. All parameters must
+ * not be null.
+ *
+ * @param post
+ * post parameters.
+ * @param loader.
+ * Resources loader.
+ * @param auth
+ * true when user has credentials to load full images.
+ * @param url
+ * image url.
+ * @return an open input stream instance (don't forget to close it).
+ * @throws IOException
+ * when a read/write error occured.
+ */
+ public InputStream openInputStream(final serverObjects post, final LoaderDispatcher loader,
+ final boolean auth, DigestURL url) throws IOException {
+ InputStream inStream = null;
+ if (url != null) {
+ try {
+ String agentName = post.get("agentName", auth ? ClientIdentification.yacyIntranetCrawlerAgentName
+ : ClientIdentification.yacyInternetCrawlerAgentName);
+ ClientIdentification.Agent agent = ClientIdentification.getAgent(agentName);
+ inStream = loader.openInputStream(loader.request(url, false, true), CacheStrategy.IFEXIST,
+ BlacklistType.SEARCH, agent);
+ } catch (final IOException e) {
+ /** No need to log full stack trace (in most cases resource is not available because of a network error) */
+ ConcurrentLog.fine("ImageViewer", "cannot load image. URL : " + url.toNormalform(true));
+ throw e;
+ }
+ }
+ if (inStream == null) {
+ throw new IOException("Input stream could no be open");
+ }
+ return inStream;
+ }
+
+ /**
+ * @param formatName
+ * informal file format name. For example : "png".
+ * @return true when image format will be rendered by browser and not by a YaCy service
+ */
+ public static boolean isBrowserRendered(String formatName) {
+ /*
+ * gif images are not loaded because of an animated gif bug within jvm
+ * which sends java into an endless loop with high CPU
+ */
+ /*
+ * svg images not supported by jdk, but by most browser, deliver just
+ * content (without crop/scale)
+ */
+ return ("gif".equalsIgnoreCase(formatName) || "svg".equalsIgnoreCase(formatName));
+ }
+
+ /**
+ * Process source image to try to produce an EncodedImage instance
+ * eventually scaled and clipped depending on post parameters. When
+ * processed, imageInStream is closed.
+ *
+ * @param post
+ * request post parameters. Must not be null.
+ * @param auth
+ * true when access rigths are OK.
+ * @param url
+ * image source URL. Must not be null.
+ * @param ext
+ * target image file format. May be null.
+ * @param imageInStream
+ * open stream on image content. Must not be null.
+ * @return an EncodedImage instance.
+ * @throws IOException
+ * when image could not be parsed or encoded to specified format.
+ */
+ public EncodedImage parseAndScale(serverObjects post, boolean auth, DigestURL url, String ext,
+ ImageInputStream imageInStream) throws IOException {
+ EncodedImage encodedImage;
+
+ // BufferedImage image = ImageIO.read(imageInStream);
+ Iterator<ImageReader> readers = ImageIO.getImageReaders(imageInStream);
+ if (!readers.hasNext()) {
+ try {
+ /* When no reader can be found, we have to close the stream */
+ imageInStream.close();
+ } catch (IOException ignoredException) {
+ }
+ String urlString = url.toNormalform(false);
+ String errorMessage = "Image format (" + MultiProtocolURL.getFileExtension(urlString) + ") is not supported.";
+ ConcurrentLog.fine("ImageViewer", errorMessage + "Image URL : " + urlString);
+ /*
+ * Throw an exception, wich will end in a HTTP 500 response, better
+ * handled by browsers than an empty image
+ */
+ throw new IOException(errorMessage);
+ }
+ ImageReader reader = readers.next();
+ reader.setInput(imageInStream, true, true);
+
+ int maxwidth = post.getInt("maxwidth", 0);
+ int maxheight = post.getInt("maxheight", 0);
+ final boolean quadratic = post.containsKey("quadratic");
+ boolean isStatic = post.getBoolean("isStatic");
+ BufferedImage image = null;
+ boolean returnRaw = true;
+ if (!auth || maxwidth != 0 || maxheight != 0) {
+
+ // find original size
+ final int originWidth = reader.getWidth(0);
+ final int originHeigth = reader.getHeight(0);
+
+ // in case of not-authorized access shrink the image to
+ // prevent
+ // copyright problems, so that images are not larger than
+ // thumbnails
+ Dimension maxDimensions = calculateMaxDimensions(auth, originWidth, originHeigth, maxwidth, maxheight);
+
+ // if a quadratic flag is set, we cut the image out to be in
+ // quadratic shape
+ int w = originWidth;
+ int h = originHeigth;
+ if (quadratic && originWidth != originHeigth) {
+ Rectangle square = getMaxSquare(originHeigth, originWidth);
+ h = square.height;
+ w = square.width;
+ }
+
+ Dimension finalDimensions = calculateDimensions(w, h, maxDimensions);
+
+ if (originWidth != finalDimensions.width || originHeigth != finalDimensions.height) {
+ returnRaw = false;
+ image = readImage(reader);
+ if (quadratic && originWidth != originHeigth) {
+ image = makeSquare(image);
+ }
+ image = scale(finalDimensions.width, finalDimensions.height, image);
+ }
+ }
+ /* Image do not need to be scaled or cropped */
+ if (returnRaw) {
+ if (!reader.getFormatName().equalsIgnoreCase(ext) || imageInStream.getFlushedPosition() != 0) {
+ /*
+ * image parsing and reencoding is only needed when source image
+ * and target formats differ, or when first bytes have been discarded
+ */
+ returnRaw = false;
+ image = readImage(reader);
+ }
+ }
+ if (returnRaw) {
+ byte[] imageData = readRawImage(imageInStream);
+ encodedImage = new EncodedImage(imageData, ext, isStatic);
+ } else {
+ /*
+ * An error can still occur when transcoding from buffered image to
+ * target ext : in that case EncodedImage.getImage() is empty.
+ */
+ encodedImage = new EncodedImage(image, ext, isStatic);
+ if (encodedImage.getImage().length() == 0) {
+ String errorMessage = "Image could not be encoded to format : " + ext;
+ ConcurrentLog.fine("ImageViewer", errorMessage + ". Image URL : " + url.toNormalform(false));
+ throw new IOException(errorMessage);
+ }
+ }
+
+ return encodedImage;
+ }
+
+ /**
+ * Read image using specified reader and close ImageInputStream source.
+ * Input must have bean set before using
+ * {@link ImageReader#setInput(Object)}
+ *
+ * @param reader
+ * image reader. Must not be null.
+ * @return buffered image
+ * @throws IOException
+ * when an error occured
+ */
+ private BufferedImage readImage(ImageReader reader) throws IOException {
+ BufferedImage image;
+ try {
+ image = reader.read(0);
+ } finally {
+ reader.dispose();
+ Object input = reader.getInput();
+ if (input instanceof ImageInputStream) {
+ try {
+ ((ImageInputStream) input).close();
+ } catch (IOException ignoredException) {
+ }
+ }
+ }
+ return image;
+ }
+
+ /**
+ * Read image data without parsing.
+ *
+ * @param inStream
+ * image source. Must not be null. First bytes must not have been marked discarded ({@link ImageInputStream#getFlushedPosition()} must be zero)
+ * @return image data as bytes
+ * @throws IOException
+ * when a read/write error occured.
+ */
+ private byte[] readRawImage(ImageInputStream inStream) throws IOException {
+ byte[] buffer = new byte[4096];
+ int l = 0;
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ inStream.seek(0);
+ try {
+ while ((l = inStream.read(buffer)) >= 0) {
+ outStream.write(buffer, 0, l);
+ }
+ return outStream.toByteArray();
+ } finally {
+ try {
+ inStream.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Calculate image dimensions from image original dimensions, max
+ * dimensions, and target dimensions.
+ *
+ * @return dimensions to render image
+ */
+ protected Dimension calculateDimensions(final int originWidth, final int originHeight, final Dimension max) {
+ int resultWidth;
+ int resultHeight;
+ if (max.width < originWidth || max.height < originHeight) {
+ // scale image
+ final double hs = (originWidth <= max.width) ? 1.0 : ((double) max.width) / ((double) originWidth);
+ final double vs = (originHeight <= max.height) ? 1.0 : ((double) max.height) / ((double) originHeight);
+ final double scale = Math.min(hs, vs);
+ // if (!auth) scale = Math.min(scale, 0.6); // this is for copyright
+ // purpose
+ if (scale < 1.0) {
+ resultWidth = Math.max(1, (int) (originWidth * scale));
+ resultHeight = Math.max(1, (int) (originHeight * scale));
+ } else {
+ resultWidth = Math.max(1, originWidth);
+ resultHeight = Math.max(1, originHeight);
+ }
+
+ } else {
+ // do not scale
+ resultWidth = originWidth;
+ resultHeight = originHeight;
+ }
+ return new Dimension(resultWidth, resultHeight);
+ }
+
+ /**
+ * Calculate image maximum dimentions from original and specified maximum
+ * dimensions
+ *
+ * @param auth
+ * true when acces rigths are OK.
+ * @return maximum dimensions to render image
+ */
+ protected Dimension calculateMaxDimensions(final boolean auth, final int originWidth, final int originHeight,
+ final int maxWidth, final int maxHeight) {
+ int resultWidth;
+ int resultHeight;
+ // in case of not-authorized access shrink the image to prevent
+ // copyright problems, so that images are not larger than thumbnails
+ if (auth) {
+ resultWidth = (maxWidth == 0) ? originWidth : maxWidth;
+ resultHeight = (maxHeight == 0) ? originHeight : maxHeight;
+ } else if ((originWidth > 16) || (originHeight > 16)) {
+ resultWidth = Math.min(96, originWidth);
+ resultHeight = Math.min(96, originHeight);
+ } else {
+ resultWidth = 16;
+ resultHeight = 16;
+ }
+ return new Dimension(resultWidth, resultHeight);
+ }
+
+ /**
+ * Scale image to specified dimensions
+ *
+ * @param width
+ * target width
+ * @param height
+ * target height
+ * @param image
+ * image to scale. Must not be null.
+ * @return a scaled image
+ */
+ public BufferedImage scale(final int width, final int height, final BufferedImage image) {
+ // compute scaled image
+ Image scaled = image.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
+ final MediaTracker mediaTracker = new MediaTracker(new Container());
+ mediaTracker.addImage(scaled, 0);
+ try {
+ mediaTracker.waitForID(0);
+ } catch (final InterruptedException e) {
+ }
+
+ // make a BufferedImage out of that
+ BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ try {
+ result.createGraphics().drawImage(scaled, 0, 0, width, height, null);
+ // check outcome
+ final Raster raster = result.getData();
+ int[] pixel = new int[raster.getSampleModel().getNumBands()];
+ pixel = raster.getPixel(0, 0, pixel);
+ } catch (final Exception e) {
+ /*
+ * Exception may be caused by source image color model : try now to
+ * convert to RGB before scaling
+ */
+ try {
+ BufferedImage converted = EncodedImage.convertToRGB(image);
+ scaled = converted.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
+ mediaTracker.addImage(scaled, 1);
+ try {
+ mediaTracker.waitForID(1);
+ } catch (final InterruptedException e2) {
+ }
+ result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ result.createGraphics().drawImage(scaled, 0, 0, width, height, null);
+
+ // check outcome
+ final Raster raster = result.getData();
+ int[] pixel = new int[result.getSampleModel().getNumBands()];
+ pixel = raster.getPixel(0, 0, pixel);
+ } catch (Exception e2) {
+ result = image;
+ }
+
+ ConcurrentLog.fine("ImageViewer", "Image could not be scaled");
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param h
+ * image height
+ * @param w
+ * image width
+ * @return max square area fitting inside dimensions
+ */
+ public Rectangle getMaxSquare(final int h, final int w) {
+ Rectangle square;
+ if (w > h) {
+ final int offset = (w - h) / 2;
+ square = new Rectangle(offset, 0, h, h);
+ } else {
+ final int offset = (h - w) / 2;
+ square = new Rectangle(0, offset, w, w);
+ }
+ return square;
+ }
+
+ /**
+ * Crop image to make a square
+ *
+ * @param image
+ * image to crop
+ * @return
+ */
+ public BufferedImage makeSquare(BufferedImage image) {
+ final int w = image.getWidth();
+ final int h = image.getHeight();
+ if (w > h) {
+ final BufferedImage dst = new BufferedImage(h, h, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = dst.createGraphics();
+ final int offset = (w - h) / 2;
+ try {
+ g.drawImage(image, 0, 0, h - 1, h - 1, offset, 0, h + offset, h - 1, null);
+ } finally {
+ g.dispose();
+ }
+ image = dst;
+ } else {
+ final BufferedImage dst = new BufferedImage(w, w, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = dst.createGraphics();
+ final int offset = (h - w) / 2;
+ try {
+ g.drawImage(image, 0, 0, w - 1, w - 1, 0, offset, w - 1, w + offset, null);
+ } finally {
+ g.dispose();
+ }
+ image = dst;
+ }
+ return image;
+ }
+}
diff --git a/test/ViewImagePerfTest.java b/test/ImageViewerPerfTest.java
index 5a13e30a3..6f758164c 100755
--- a/test/ViewImagePerfTest.java
+++ b/test/ImageViewerPerfTest.java
@@ -1,21 +1,4 @@
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.imageio.ImageIO;
-import javax.imageio.stream.ImageInputStream;
-
-import net.yacy.cora.util.ConcurrentLog;
-import net.yacy.peers.graphics.EncodedImage;
-import net.yacy.server.serverObjects;
-
-// ViewImagePerfTest.java
+// ImageViewerPerfTest.java
// -----------------------
// part of YaCy
// (C) by Michael Peter Christen; mc@yacy.net
@@ -37,13 +20,33 @@ import net.yacy.server.serverObjects;
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageInputStream;
+
+import net.yacy.cora.document.id.DigestURL;
+import net.yacy.cora.util.ConcurrentLog;
+import net.yacy.peers.graphics.EncodedImage;
+import net.yacy.server.serverObjects;
+
+
+
/**
- * Test to measure image render performance by ViewImage
+ * Test to measure image render performance by
*
* @author luc
*
*/
-public class ViewImagePerfTest extends ViewImageTest {
+public class ImageViewerPerfTest extends ImageViewerTest {
/** Default minimum measurement time */
private static final int DEFAULT_MIN_MEASURE_TIME = 10;
@@ -56,7 +59,7 @@ public class ViewImagePerfTest extends ViewImageTest {
* main parameters : args[7] may contain minimum measurement time
* in secondes. Default : 10.
*/
- public ViewImagePerfTest(String args[]) {
+ public ImageViewerPerfTest(String args[]) {
this.minMeasureTime = getMinMeasurementTime(args);
}
@@ -87,7 +90,7 @@ public class ViewImagePerfTest extends ViewImageTest {
* @param outDir
* output directory
* @param post
- * ViewImage post parameters
+ * post parameters
* @param failures
* map failed file urls to eventual exception
* @param inFile
@@ -100,7 +103,7 @@ public class ViewImagePerfTest extends ViewImageTest {
File inFile) throws IOException {
/* Delete eventual previous result file */
System.out
- .println("Measuring ViewImage render with file : " + inFile.getAbsolutePath() + " encoded To : " + ext);
+ .println("Measuring render with file : " + inFile.getAbsolutePath() + " encoded To : " + ext);
File outFile = new File(outDir, inFile.getName() + "." + ext);
if (outFile.exists()) {
outFile.delete();
@@ -115,7 +118,7 @@ public class ViewImagePerfTest extends ViewImageTest {
beginTime = System.nanoTime();
ImageInputStream inStream = ImageIO.createImageInputStream(inFile);
try {
- img = ViewImage.parseAndScale(post, true, urlString, ext, inStream);
+ img = this.VIEWER.parseAndScale(post, true, new DigestURL(urlString), ext, inStream);
} catch (Exception e) {
error = e;
}
@@ -131,7 +134,7 @@ public class ViewImagePerfTest extends ViewImageTest {
}
PrintWriter resultsWriter = new PrintWriter(new FileWriter(new File(outDir, "results_perfs.txt"), true));
try {
- writeMessage("Measured ViewImage render with file : " + inFile.getAbsolutePath() + " encoded To : " + ext,
+ writeMessage("Measured render with file : " + inFile.getAbsolutePath() + " encoded To : " + ext,
resultsWriter);
if(img == null) {
writeMessage("Image could not be rendered! Measurement show time needed to read and parse image data until error detection.", resultsWriter);
@@ -176,7 +179,7 @@ public class ViewImagePerfTest extends ViewImageTest {
* <li>args[1] : output format name (for example : "jpg") for
* rendered image. Defaut : "png".</li>
* <li>args[2] : ouput folder URL. Default :
- * "[system tmp dir]/ViewImageTest".</li>
+ * "[system tmp dir]/Test".</li>
* <li>args[3] : max width (in pixels) for rendered image. May be
* set to zero to specify no max width. Default : no value.</li>
* <li>args[4] : max height (in pixels) for rendered image. May
@@ -194,7 +197,7 @@ public class ViewImagePerfTest extends ViewImageTest {
* when a read/write error occured
*/
public static void main(String args[]) throws IOException {
- ViewImagePerfTest test = new ViewImagePerfTest(args);
+ ImageViewerPerfTest test = new ImageViewerPerfTest(args);
File inFile = test.getInputURL(args);
String ext = test.getEncodingExt(args);
File outDir = test.getOuputDir(args);
@@ -207,10 +210,10 @@ public class ViewImagePerfTest extends ViewImageTest {
inFiles = new File[1];
inFiles[0] = inFile;
System.out.println(
- "Measuring ViewImage render with file : " + inFile.getAbsolutePath() + " encoded To : " + ext);
+ "Measuring render with file : " + inFile.getAbsolutePath() + " encoded To : " + ext);
} else if (inFile.isDirectory()) {
inFiles = inFile.listFiles();
- System.out.println("Measuring ViewImage render with files in folder : " + inFile.getAbsolutePath()
+ System.out.println("Measuring render with files in folder : " + inFile.getAbsolutePath()
+ " encoded To : " + ext);
} else {
inFiles = new File[0];
diff --git a/test/ViewImageTest.java b/test/ImageViewerTest.java
index a80a7d588..894067f5e 100755
--- a/test/ViewImageTest.java
+++ b/test/ImageViewerTest.java
@@ -1,23 +1,4 @@
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-
-import javax.imageio.ImageIO;
-import javax.imageio.stream.ImageInputStream;
-
-import net.yacy.cora.util.ConcurrentLog;
-import net.yacy.peers.graphics.EncodedImage;
-import net.yacy.server.serverObjects;
-
-// ViewImageTest.java
+// ImageViewerTest.java
// -----------------------
// part of YaCy
// (C) by Michael Peter Christen; mc@yacy.net
@@ -39,19 +20,43 @@ import net.yacy.server.serverObjects;
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageInputStream;
+
+import net.yacy.cora.document.id.DigestURL;
+import net.yacy.cora.util.ConcurrentLog;
+import net.yacy.peers.graphics.EncodedImage;
+import net.yacy.server.serverObjects;
+import net.yacy.visualization.ImageViewer;
+
/**
- * Test rendering of one or more image files by ViewImage
+ * Test rendering of one or more image files by ImageViewer
*
* @author luc
*
*/
-public class ViewImageTest {
+public class ImageViewerTest {
/** Default image */
private static final String DEFAULT_IMG_RESOURCES = "/viewImageTest/test";
/** Default output encoding format */
private static final String DEFAULT_OUT_EXT = "png";
+
+ /** Viewer instance */
+ protected final ImageViewer VIEWER = new ImageViewer();
/**
* @param args
@@ -65,7 +70,7 @@ public class ViewImageTest {
if (args != null && args.length > 0) {
fileURL = args[0];
} else {
- URL defaultURL = ViewImageTest.class.getResource(DEFAULT_IMG_RESOURCES);
+ URL defaultURL = ImageViewerTest.class.getResource(DEFAULT_IMG_RESOURCES);
if (defaultURL == null) {
throw new IllegalArgumentException("File not found : " + DEFAULT_IMG_RESOURCES);
}
@@ -97,7 +102,7 @@ public class ViewImageTest {
}
/**
- * Build post parameters to use with ViewImage
+ * Build post parameters to use with ImageViewer
*
* @param args
* main parameters : args[3] and args[4] may respectively contain
@@ -238,7 +243,7 @@ public class ViewImageTest {
* @param outDir
* output directory
* @param post
- * ViewImage post parameters
+ * ImageViewer post parameters
* @param inFiles
* files or directories to process
* @param processedFiles
@@ -269,7 +274,7 @@ public class ViewImageTest {
* parameters must not be null.
* @param ext output encoding image format
* @param outDir output directory
- * @param post ViewImage post parameters
+ * @param post ImageViewer post parameters
* @param failures map failed file urls to eventual exception
* @param inFile file image to process
* @throws IOException when an read/write error occured
@@ -287,7 +292,7 @@ public class ViewImageTest {
EncodedImage img = null;
Throwable error = null;
try {
- img = ViewImage.parseAndScale(post, true, urlString, ext, inStream);
+ img = this.VIEWER.parseAndScale(post, true, new DigestURL(urlString), ext, inStream);
} catch (Throwable e) {
error = e;
}
@@ -322,7 +327,7 @@ public class ViewImageTest {
* <li>args[1] : output format name (for example : "jpg") for
* rendered image. Defaut : "png".</li>
* <li>args[2] : ouput folder URL. Default :
- * "[system tmp dir]/ViewImageTest".</li>
+ * "[system tmp dir]/ImageViewerTest".</li>
* <li>args[3] : max width (in pixels) for rendered image. May be
* set to zero to specify no max width. Default : no value.</li>
* <li>args[4] : max height (in pixels) for rendered image. May
@@ -338,7 +343,7 @@ public class ViewImageTest {
* when a read/write error occured
*/
public static void main(String args[]) throws IOException {
- ViewImageTest test = new ViewImageTest();
+ ImageViewerTest test = new ImageViewerTest();
File inFile = test.getInputURL(args);
String ext = test.getEncodingExt(args);
File outDir = test.getOuputDir(args);
@@ -350,11 +355,11 @@ public class ViewImageTest {
if (inFile.isFile()) {
inFiles = new File[1];
inFiles[0] = inFile;
- System.out.println("Testing ViewImage rendering with input file : " + inFile.getAbsolutePath()
+ System.out.println("Testing ImageViewer rendering with input file : " + inFile.getAbsolutePath()
+ " encoded To : " + ext);
} else if (inFile.isDirectory()) {
inFiles = inFile.listFiles();
- System.out.println("Testing ViewImage rendering with input files in folder : " + inFile.getAbsolutePath()
+ System.out.println("Testing ImageViewer rendering with input files in folder : " + inFile.getAbsolutePath()
+ " encoded To : " + ext);
} else {
inFiles = new File[0];
diff --git a/test/java/yacysearchitemTest.java b/test/java/yacysearchitemTest.java
index ef28d519e..c79a94030 100644
--- a/test/java/yacysearchitemTest.java
+++ b/test/java/yacysearchitemTest.java
@@ -155,6 +155,26 @@ public class yacysearchitemTest {
Assert.assertNotNull(faviconURL);
Assert.assertEquals("http://somehost.org/static/images/favicon.ico", faviconURL.toNormalform(false));
}
+
+ /**
+ * One non-standard icon with no size
+ *
+ * @throws MalformedURLException
+ */
+ @Test
+ public final void testGetFaviconURLNonStandardNoSize() throws MalformedURLException {
+ URIMetadataNode metadataNode = new URIMetadataNode(new DigestURL("http://somehost.org"));
+ metadataNode.setField(CollectionSchema.icons_urlstub_sxt.getSolrFieldName(),
+ new String[] { "somehost.org/static/images/favicon.png" });
+ List<String> protocols = CollectionConfiguration
+ .protocolList2indexedList(Arrays.asList(new String[] { "http" }));
+ metadataNode.setField(CollectionSchema.icons_protocol_sxt.getSolrFieldName(), protocols);
+ metadataNode.setField(CollectionSchema.icons_rel_sxt.getSolrFieldName(), new String[] { "appel-touch-icon" });
+
+ DigestURL faviconURL = yacysearchitem.getFaviconURL(metadataNode, new Dimension(32, 32));
+ Assert.assertNotNull(faviconURL);
+ Assert.assertEquals("http://somehost.org/static/images/favicon.png", faviconURL.toNormalform(false));
+ }
/**
* No icon in document