package freenet.clients.http; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.font.FontRenderContext; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.sql.Date; import java.text.ParseException; import javax.imageio.ImageIO; import freenet.client.HighLevelSimpleClient; import freenet.support.MultiValueTable; import freenet.support.api.Bucket; import freenet.support.api.HTTPRequest; /** This toadlet creates a PNG image with the specified text. */ public class ImageCreatorToadlet extends Toadlet { /** The default width */ public static final int DEFAULT_WIDTH = 100; /** The default height */ public static final int DEFAULT_HEIGHT = 100; /** * The last modification time of the class, it is required for the * client-side cache. * If anyone makes modifications to this class, this needs to be updated. */ public static final Date LAST_MODIFIED = new Date(1248256659000l); protected ImageCreatorToadlet(HighLevelSimpleClient client) { super(client); } public void handleMethodGET(URI uri, HTTPRequest req, ToadletContext ctx) throws ToadletContextClosedException, IOException, RedirectException { boolean needsGeneration = true; // If the browser has requested this image, then it will send this header if (ctx.getHeaders().containsKey("if-modified-since")) { try { // If the received date is equal to the last modification of this class, then it doesn't need regeneration if (ToadletContextImpl.parseHTTPDate(ctx.getHeaders().get("if-modified-since")).compareTo(LAST_MODIFIED) == 0) { // So we just send the NOT_MODIFIED response, and skip the generation ctx.sendReplyHeadersStatic(304, "Not Modified", null, "image/png", 0, LAST_MODIFIED); needsGeneration = false; } } catch (ParseException pe) { // If something goes wrong, we regenerate } } if (needsGeneration) { // The text that will be drawn String text = req.getParam("text"); // If width or height is specified, we use it, if not, then we use the default int requiredWidth = req.getParam("width").compareTo("") != 0 ? Integer.parseInt(req.getParam("width").endsWith("px")?req.getParam("width").substring(0, req.getParam("width").length()-2):req.getParam("width")) : DEFAULT_WIDTH; int requiredHeight = req.getParam("height").compareTo("") != 0 ? Integer.parseInt(req.getParam("height").endsWith("px")?req.getParam("height").substring(0, req.getParam("height").length()-2):req.getParam("height")) : DEFAULT_HEIGHT; // This is the image we are making BufferedImage buffer = new BufferedImage(requiredWidth, requiredHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = buffer.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); FontRenderContext fc = g2.getFontRenderContext(); // We then specify the maximum font size that fits in the image // For this, we start at 1, and increase it, until it overflows. This-1 will be the font size float size = 1; g2.setFont(g2.getFont().deriveFont(size)); int width = 0; int height = 0; while (width < requiredWidth && height < requiredHeight) { Rectangle2D bounds = g2.getFont().getStringBounds(text, fc); // calculate the size of the text width = (int) bounds.getWidth(); height = (int) bounds.getHeight(); g2.setFont(g2.getFont().deriveFont(++size)); } g2.setFont(g2.getFont().deriveFont(size - 1)); Rectangle2D bounds = g2.getFont().getStringBounds(text, fc); // actually do the drawing g2.setColor(new Color(0, 0, 0)); g2.fillRect(0, 0, width, height); g2.setColor(new Color(255, 255, 255)); // We position it to the center. Note that this is not the upper left corner g2.drawString(text, (int) (requiredWidth / 2 - bounds.getWidth() / 2), (int) (requiredHeight / 2 + bounds.getHeight() / 4)); // Write the data, and send the modification data to let the client cache it Bucket data = ctx.getBucketFactory().makeBucket(-1); OutputStream os = data.getOutputStream(); try { ImageIO.write(buffer, "png", os); } finally { os.close(); } MultiValueTable<String, String> headers=new MultiValueTable<String, String>(); ctx.sendReplyHeadersStatic(200, "OK", headers, "image/png", data.size(), LAST_MODIFIED); ctx.writeData(data); } } @Override public String path() { return "/imagecreator/"; } }