/******************************************************************************* * Copyright 2013-2016 RTR-GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package at.alladin.rmbt.statisticServer.export; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.text.FieldPosition; import java.text.Format; import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.representation.OutputRepresentation; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.resource.Get; import at.alladin.rmbt.shared.SignificantFormat; import at.alladin.rmbt.statisticServer.ServerResource; /** * * @author ths */ public class ImageExport extends ServerResource { @Get public Representation retrieve(final String entity) { if (!getRequest().getAttributes().containsKey("lang") || !getRequest().getAttributes().containsKey("size")) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return new StringRepresentation("illegal parameters"); } if (!getRequest().getAttributes().containsKey("open_test_uuid")) { return new StringRepresentation("invalid uuid"); } final String uuid = getRequest().getAttributes().get("open_test_uuid").toString().substring(1); //since the first letter is a 'O' final String lang = getRequest().getAttributes().get("lang").toString(); final String size = getRequest().getAttributes().get("size").toString(); if (!lang.equals("de") && !lang.equals("en")) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return new StringRepresentation("invalid language"); } if (!size.equals("forumlarge") && !size.equals("forumsmall") && !size.equals("thumbnail")) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return new StringRepresentation("invalid image type"); } //first - get data final String sql = "SELECT" //+ " ('P' || t.open_uuid) open_uuid," //+ " to_char(t.time AT TIME ZONE 'UTC', 'YYYY-MM-DD HH24:MI') \"time\"," + " nt.name network_type," //+ " t.geo_provider loc_src," //+ " t.zip_code," + " t.speed_download download_kbit," + " t.speed_upload upload_kbit," + " (t.ping_median::float / 1000000) ping_ms," + " t.signal_strength," + " COALESCE(prov.shortname, mprov.shortname, prov.name, mprov.name, network_operator_name, network_sim_operator) provider_name," + " COALESCE(t.plattform, t.client_name) as platform " //+ " network_operator network_mcc_mnc," //+ " network_operator_name network_name," //+ " network_sim_operator sim_mcc_mnc," //+ " nat_type \"connection\"," //+ " public_ip_asn asn," //+ " client_public_ip_anonymized ip_anonym," //+ " (ndt.s2cspd*1000)::int ndt_download_kbit," //+ " (ndt.c2sspd*1000)::int ndt_upload_kbit" + " FROM test t" + " LEFT JOIN network_type nt ON nt.uid=t.network_type" + " LEFT JOIN device_map adm ON adm.codename=t.model" + " LEFT JOIN test_server ts ON ts.uid=t.server_id" + " LEFT JOIN test_ndt ndt ON t.uid=ndt.test_id" + " LEFT JOIN provider prov ON mobile_provider_id = prov.uid " + " LEFT JOIN provider mprov ON provider_id = mprov.uid" + " WHERE " + " t.deleted = false AND t.implausible = false" + " AND status = 'FINISHED'" + " AND open_test_uuid = ?"; PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); ps.setObject(1, uuid, Types.OTHER); rs = ps.executeQuery(); if (!rs.next()) { setStatus(Status.CLIENT_ERROR_NOT_FOUND); return new StringRepresentation("invalid uuid"); } final double download = ((double) rs.getInt("download_kbit")) / 1000; final double upload = ((double) rs.getInt("upload_kbit")) / 1000; final double ping = rs.getFloat("ping_ms"); final String isp = rs.getString("provider_name"); final String typ = rs.getString("network_type"); final String signal = rs.getString("signal_strength"); final String os = rs.getString("platform"); final OutputRepresentation result = new OutputRepresentation(MediaType.IMAGE_PNG) { @Override public void write(OutputStream out) throws IOException { ShareImageGenerator generator; if (size.equals("thumbnail")) { generator = new FacebookThumbnailGenerator(); } else if (size.equals("forumsmall")){ generator = new ForumBannerSmallGenerator(); } else { generator = new ForumBannerGenerator(); } BufferedImage img = generator.generateImage(lang, upload, download, ping, isp, typ, signal, os); ImageIO.write(img, "png", out); } }; return result; } catch (SQLException e) { Logger.getLogger(ImageExport.class.getName()).log(Level.SEVERE, null, e); setStatus(Status.CLIENT_ERROR_NOT_FOUND); return new StringRepresentation("invalid uuid"); } } public abstract class ShareImageGenerator { /** * Generates a image for showing the user its speed test result * @param lang: Language of the image, currently either 'de' or 'en' * @param upload: Upload speed in mbps * @param download: Download speed in mbps * @param ping: Ping in ms * @param isp: ISP name * @param typ: Test type (LAN, 3G, 4G, etc.) * @param signal: Signal strength in dbm * @param os: Plattform used for conducting the test (Android, IOS, Applet, Browser) * @return the image OR null, if parameters are incorrect */ public abstract BufferedImage generateImage(String lang, double upload, double download, double ping, String isp, String typ, String signal, String os) throws IOException; /** * Formats a number to 2 significant digits * @param number the number * @return the formatted number */ protected String formatNumber(double number, String lang) { final Locale locale = new Locale(lang); final Format format = new SignificantFormat(2, locale); final StringBuffer buf = format.format(number, new StringBuffer(), new FieldPosition(0)); return buf.toString(); } protected void drawCenteredString(String s, int x, int y, int w, int h, Graphics g) { FontMetrics fm = g.getFontMetrics(); x += (w - fm.stringWidth(s)) / 2; y += (fm.getAscent() + (h - (fm.getAscent() + fm.getDescent())) / 2); g.drawString(s, x, y); } } public class ForumBannerGenerator extends ShareImageGenerator { @Override public BufferedImage generateImage(String lang, double upload, double download, double ping, String isp, String typ, String signal, String os) throws IOException{ String unknownString = (lang.equals("de")) ? "unbekannt" : "unknown"; BufferedImage img = new BufferedImage(600, 200, BufferedImage.TYPE_INT_ARGB); img.createGraphics(); Graphics2D g = (Graphics2D)img.getGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BufferedImage img2 = ImageIO.read(getClass().getResourceAsStream("forumbanner_" + lang + ".png")); g.drawImage(img2, null, 0, 0); //Speeds g.setColor(Color.black); g.setFont(new Font("Droid Sans", Font.BOLD, 60)); g.drawString(formatNumber(download, lang), 30, 105); g.drawString(formatNumber(upload, lang), 230, 105); g.drawString(formatNumber(ping, lang), 440, 105); //ISP and other information g.setColor(Color.WHITE); g.setFont(new Font("Verdana", Font.BOLD,16)); //de if (lang.equals("de")) { //left g.drawString((typ == null)?unknownString:typ, 110, 168); g.drawString((isp == null)?unknownString:isp, 110, 191); //right g.drawString((signal==null)?unknownString:signal + " dBm", 410, 168); g.drawString((os==null)?unknownString:os, 410, 191); } else { //en //left g.drawString((typ == null)?unknownString:typ, 130, 168); g.drawString((isp == null)?unknownString:isp, 90, 191); //right g.drawString((signal==null)?unknownString:signal + " dBm", 445, 168); g.drawString((os==null)?unknownString:os, 445, 191); } return img; } } public class ForumBannerSmallGenerator extends ShareImageGenerator { @Override public BufferedImage generateImage(String lang, double upload, double download, double ping, String isp, String typ, String signal, String os) throws IOException { String unknownString = (lang.equals("de")) ? "unbekannt" : "unknown"; BufferedImage img = new BufferedImage(390, 130, BufferedImage.TYPE_INT_ARGB); img.createGraphics(); Graphics2D g = (Graphics2D)img.getGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BufferedImage img2 = ImageIO.read(getClass().getResourceAsStream("forumsmall_" + lang + ".png")); g.drawImage(img2, null, 0, 0); //Speeds g.setColor(Color.black); g.setFont(new Font("Droid Sans", Font.BOLD, 40)); drawCenteredString(formatNumber(download, lang), 0, 20,130,65,g); drawCenteredString(formatNumber(upload, lang), 130, 20,130,65,g); drawCenteredString(formatNumber(ping, lang), 260, 20,130,65,g); //ISP and other information g.setColor(Color.WHITE); g.setFont(new Font("Verdana", Font.BOLD,10)); //de if (lang.equals("de")) { //left g.drawString((typ == null)?unknownString:typ, 73, 109); g.drawString((isp == null)?unknownString:isp, 73, 124); //right g.drawString((signal==null)?"":signal + " dBm", 270, 109); g.drawString((os==null)?unknownString:os, 270, 124); //hide signal caption if signal is null if (signal==null) { g.setColor(new Color(89,178,0)); g.fillRect(195, 98, 71, 13); } } else { //en //left g.drawString((typ == null)?unknownString:typ, 83, 109); g.drawString((isp == null)?unknownString:isp, 60, 124); //right g.drawString((signal==null)?"":signal + " dBm", 290, 109); g.drawString((os==null)?unknownString:os, 290, 124); //hide signal caption if signal is null if (signal==null) { g.setColor(new Color(89,178,0)); g.fillRect(195, 98, 90, 13); } } g.dispose(); return img; } } public class FacebookThumbnailGenerator extends ShareImageGenerator { @Override public BufferedImage generateImage(String lang, double upload, double download, double ping, String isp, String typ, String signal, String os) throws IOException { BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); img.createGraphics(); Graphics2D g = (Graphics2D)img.getGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BufferedImage img2 = ImageIO.read(getClass().getResourceAsStream("netztest-thumbnail.png")); g.drawImage(img2, null, 0, 0); //Speeds g.setColor(Color.white); g.setFont(new Font("Droid Sans", Font.PLAIN, 35)); String up = formatNumber(upload, lang); drawCenteredString(up, 25, 48, 80, 54, g); String down = formatNumber(download, lang); drawCenteredString(down, 0, 0, 80, 54, g); g.dispose(); return img; } } }