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.
pull/39/head
luc 9 years ago
parent 07222b3e1a
commit 26f1ead57c

@ -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;
}
}

@ -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,52 +75,22 @@ 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 {
EncodedImage encodedImage;
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);
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
@ -154,33 +99,23 @@ public class ViewImage {
if (url.isFile()) {
imageInStream = ImageIO.createImageInputStream(url.getFSFile());
} else {
inStream = openInputStream(post, sb.loader, auth, url);
inStream = VIEWER.openInputStream(post, sb.loader, auth, url);
imageInStream = ImageIO.createImageInputStream(inStream);
}
// read image
encodedImage = parseAndScale(post, auth, urlString, ext, imageInStream);
encodedImage = VIEWER.parseAndScale(post, auth, url, 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 {
/*
* 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(new byte[0], ext, post.getBoolean("isStatic"));
}
} finally {
/*
* imageInStream.close() method doesn't close source input
* stream
* imageInStream.close() method doesn't close source input stream
*/
if (inStream != null) {
try {
@ -189,412 +124,10 @@ public class ViewImage {
}
}
}
}
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) {
}
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
*/
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 (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("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);
} 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
*/
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("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;
}
/**
* 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;
}
}

@ -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 = "";

@ -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;

@ -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;
}
}

@ -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];

@ -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,13 +20,34 @@ 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";
@ -53,6 +55,9 @@ public class ViewImageTest {
/** Default output encoding format */
private static final String DEFAULT_OUT_EXT = "png";
/** Viewer instance */
protected final ImageViewer VIEWER = new ImageViewer();
/**
* @param args
* main parameters. first item may contain input file or folder
@ -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];

@ -156,6 +156,26 @@ public class yacysearchitemTest {
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
*

Loading…
Cancel
Save