summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluc <luc@debianluc>2016-02-09 20:46:44 +0100
committerluc <luc@debianluc>2016-02-09 20:46:44 +0100
commit26f1ead57c2b6568e2b0a45f57423ed998074049 (patch)
tree8b8d167f3071759c95ca0b7769fa5714236c5f08
parent07222b3e1a25d0c8b882bbc345d85bd46bb45c84 (diff)
Created ViewFavicon class specialized in favicon viewing.
Main image processing is now in ImageViewer, used by both ViewImage and ViewFavicon. Fixed URIMetadataNode.getFavicon to use non-standard icons with no size ass fallback.
-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