forked from I2P_Developers/i2p.i2p
New imagegen webapp, including
servlets for identicon and qr images. Heavily modified from the example in the identicon package, which is MIT licensed.
This commit is contained in:
2
apps/imagegen/imagegen/README.txt
Normal file
2
apps/imagegen/imagegen/README.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Servlets based on the example from the identicon package.
|
||||
License: See ../identicon/README.md
|
114
apps/imagegen/imagegen/build.xml
Normal file
114
apps/imagegen/imagegen/build.xml
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="imagegen">
|
||||
<property name="project" value="imagegen" />
|
||||
<property name="jetty" value="../../jetty/" />
|
||||
<property name="lib" value="${jetty}/jettylib" />
|
||||
<path id="cp">
|
||||
<pathelement location="${lib}/javax.servlet.jar" />
|
||||
<pathelement location="../identicon/build/identicon.jar" />
|
||||
<pathelement location="../zxing/build/zxing.jar" />
|
||||
<pathelement location="../../../build/i2p.jar" />
|
||||
</path>
|
||||
|
||||
<target name="all" depends="war" />
|
||||
<target name="build" depends="builddep, war" />
|
||||
<target name="builddep">
|
||||
<!-- run from top level build.xml to get dependencies built -->
|
||||
</target>
|
||||
<condition property="depend.available">
|
||||
<typefound name="depend" />
|
||||
</condition>
|
||||
<target name="depend" if="depend.available">
|
||||
<depend
|
||||
cache="../../../build"
|
||||
srcdir="./webapp/src/main/java"
|
||||
destdir="./build/obj" >
|
||||
</depend>
|
||||
</target>
|
||||
|
||||
<!-- only used if not set by a higher build.xml -->
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.6" />
|
||||
|
||||
<target name="compile" depends="depend">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/WEB-INF" />
|
||||
<mkdir dir="./build/WEB-INF/classes" />
|
||||
<javac srcdir="./webapp/src/main/java" debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
includeAntRuntime="false"
|
||||
classpathref="cp"
|
||||
destdir="./build/WEB-INF/classes" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="listChangedFiles" if="shouldListChanges" >
|
||||
<exec executable="mtn" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="list" />
|
||||
<arg value="changed" />
|
||||
<arg value="." />
|
||||
</exec>
|
||||
<!-- \n in an attribute value generates an invalid manifest -->
|
||||
<exec executable="tr" inputstring="${workspace.changes}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-s" />
|
||||
<arg value="[:space:]" />
|
||||
<arg value="," />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile, warUpToDate" unless="war.uptodate" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<!-- put the identicon and zxing classes in the war -->
|
||||
<copy todir="build/WEB-INF/classes">
|
||||
<fileset dir="../identicon/build/obj"/>
|
||||
<fileset dir="../zxing/build/obj"/>
|
||||
</copy>
|
||||
<war destfile="build/${project}.war" webxml="webapp/src/main/webapp/WEB-INF/web.xml">
|
||||
<fileset dir="build">
|
||||
<include name="WEB-INF/**/*.class"/>
|
||||
</fileset>
|
||||
<fileset dir="webapp/src/main/webapp"/>
|
||||
<manifest>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
||||
</manifest>
|
||||
</war>
|
||||
</target>
|
||||
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="${project}.war">
|
||||
<srcfiles dir= "." includes="WEB-INF/web-out.xml WEB-INF/**/*.class images/*.png css.css index.html WEB-INF/classes/${project}.properties" />
|
||||
</uptodate>
|
||||
<condition property="shouldListChanges" >
|
||||
<and>
|
||||
<not>
|
||||
<isset property="war.uptodate" />
|
||||
</not>
|
||||
<isset property="mtn.available" />
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/javadoc" />
|
||||
<javadoc
|
||||
sourcepath="./webapp/src/main/java" destdir="./build/javadoc"
|
||||
packagenames="*"
|
||||
use="true"
|
||||
splitindex="true"
|
||||
windowtitle="imagegen webapp" />
|
||||
</target>
|
||||
<target name="clean">
|
||||
<delete dir="./build" />
|
||||
<delete dir="./buildTest" />
|
||||
</target>
|
||||
<target name="cleandep" depends="clean">
|
||||
</target>
|
||||
<target name="distclean" depends="clean">
|
||||
</target>
|
||||
</project>
|
@@ -0,0 +1,171 @@
|
||||
package net.i2p.imagegen;
|
||||
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.docuverse.identicon.IdenticonCache;
|
||||
import com.docuverse.identicon.IdenticonRenderer;
|
||||
import com.docuverse.identicon.IdenticonUtil;
|
||||
import com.docuverse.identicon.NineBlockIdenticonRenderer2;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConvertToHash;
|
||||
|
||||
|
||||
/**
|
||||
* This servlet generates <i>identicon</i> (visual identifier) images ranging
|
||||
* from 16x16 to 512x512 in size.
|
||||
*
|
||||
* <h5>Supported Image Formats</h5>
|
||||
* <p>
|
||||
* Currently only PNG is supported because <code>javax.imageio</code> package
|
||||
* does not come with built-in GIF encoder and PNG is the only remaining
|
||||
* reasonable format.
|
||||
* </p>
|
||||
* <h5>Initialization Parameters:</h5>
|
||||
* <blockquote>
|
||||
* <dl>
|
||||
* <dt>inetSalt</dt>
|
||||
* <dd>salt used to generate identicon code with. must be fairly long.
|
||||
* (Required)</dd>
|
||||
* <dt>cacheProvider</dt>
|
||||
* <dd>full class path to <code>IdenticonCache</code> implementation.
|
||||
* (Optional)</dd>
|
||||
* </dl>
|
||||
* </blockquote>
|
||||
* <h5>Request ParametersP</h5>
|
||||
* <blockquote>
|
||||
* <dl>
|
||||
* <dt>code</dt>
|
||||
* <dd>identicon code to render. If missing, requester's IP addresses is used
|
||||
* to generated one. (Optional)</dd>
|
||||
* <dt>size</dt>
|
||||
* <dd>identicon size in pixels. If missing, a 16x16 pixels identicon is
|
||||
* returned. Minimum size is 16 and maximum is 64. (Optional)</dd>
|
||||
* </dl>
|
||||
* </blockquote>
|
||||
*
|
||||
* @author don
|
||||
* @since 0.9.25
|
||||
*/
|
||||
public class IdenticonServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = -3507466186902317988L;
|
||||
private static final String INIT_PARAM_VERSION = "version";
|
||||
private static final String INIT_PARAM_CACHE_PROVIDER = "cacheProvider";
|
||||
private static final String PARAM_IDENTICON_SIZE_SHORT = "s";
|
||||
private static final String PARAM_IDENTICON_CODE_SHORT = "c";
|
||||
private static final String IDENTICON_IMAGE_FORMAT = "PNG";
|
||||
private static final String IDENTICON_IMAGE_MIMETYPE = "image/png";
|
||||
private static final long DEFAULT_IDENTICON_EXPIRES_IN_MILLIS = 24 * 60 * 60 * 1000;
|
||||
private int version = 1;
|
||||
private final IdenticonRenderer renderer = new NineBlockIdenticonRenderer2();
|
||||
private IdenticonCache cache;
|
||||
private long identiconExpiresInMillis = DEFAULT_IDENTICON_EXPIRES_IN_MILLIS;
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig cfg) throws ServletException {
|
||||
super.init(cfg);
|
||||
|
||||
// Since identicons cache expiration is very long, version is
|
||||
// used in ETag to force identicons to be updated as needed.
|
||||
// Change veresion whenever rendering codes changes result in
|
||||
// visual changes.
|
||||
if (cfg.getInitParameter(INIT_PARAM_VERSION) != null)
|
||||
this.version = Integer.parseInt(cfg
|
||||
.getInitParameter(INIT_PARAM_VERSION));
|
||||
|
||||
String cacheProvider = cfg.getInitParameter(INIT_PARAM_CACHE_PROVIDER);
|
||||
if (cacheProvider != null) {
|
||||
try {
|
||||
Class cacheClass = Class.forName(cacheProvider);
|
||||
this.cache = (IdenticonCache) cacheClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException {
|
||||
|
||||
String codeParam = request.getParameter(PARAM_IDENTICON_CODE_SHORT);
|
||||
boolean codeSpecified = codeParam != null && codeParam.length() > 0;
|
||||
if (!codeSpecified) {
|
||||
response.setStatus(403);
|
||||
return;
|
||||
}
|
||||
String sizeParam = request.getParameter(PARAM_IDENTICON_SIZE_SHORT);
|
||||
int size = 32;
|
||||
if (sizeParam != null) {
|
||||
try {
|
||||
size = Integer.parseInt(sizeParam);
|
||||
if (size < 16)
|
||||
size = 16;
|
||||
else if (size > 512)
|
||||
size = 512;
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
String identiconETag = IdenticonUtil.getIdenticonETag(codeParam.hashCode(), size,
|
||||
version);
|
||||
String requestETag = request.getHeader("If-None-Match");
|
||||
|
||||
if (requestETag != null && requestETag.equals(identiconETag)) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
} else {
|
||||
// we try to interpret the codeParam parameter as:
|
||||
// 1) a number
|
||||
// 2) a base32 or base64 hash, which we take the Java hashcode of
|
||||
// 3) a string, which we take the Java hashcode of
|
||||
int code;
|
||||
try {
|
||||
code = Integer.parseInt(codeParam);
|
||||
} catch (NumberFormatException nfe) {
|
||||
Hash h = ConvertToHash.getHash(codeParam);
|
||||
if (h != null)
|
||||
code = Arrays.hashCode(h.getData());
|
||||
else
|
||||
code = codeParam.hashCode();
|
||||
}
|
||||
byte[] imageBytes = null;
|
||||
|
||||
// retrieve image bytes from either cache or renderer
|
||||
if (cache == null
|
||||
|| (imageBytes = cache.get(identiconETag)) == null) {
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
RenderedImage image = renderer.render(code, size);
|
||||
ImageIO.write(image, IDENTICON_IMAGE_FORMAT, byteOut);
|
||||
imageBytes = byteOut.toByteArray();
|
||||
if (cache != null)
|
||||
cache.add(identiconETag, imageBytes);
|
||||
} else {
|
||||
response.setStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
// set ETag and, if code was provided, Expires header
|
||||
response.setHeader("ETag", identiconETag);
|
||||
if (codeSpecified) {
|
||||
long expires = System.currentTimeMillis()
|
||||
+ identiconExpiresInMillis;
|
||||
response.addDateHeader("Expires", expires);
|
||||
}
|
||||
|
||||
// return image bytes to requester
|
||||
response.setContentType(IDENTICON_IMAGE_MIMETYPE);
|
||||
response.setContentLength(imageBytes.length);
|
||||
response.getOutputStream().write(imageBytes);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
package net.i2p.imagegen;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.docuverse.identicon.IdenticonCache;
|
||||
import com.docuverse.identicon.IdenticonUtil;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.client.j2se.MatrixToImageConfig;
|
||||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
|
||||
/**
|
||||
* This servlet generates QR code images.
|
||||
*
|
||||
* @author modiied from identicon
|
||||
* @since 0.9.25
|
||||
*/
|
||||
public class QRServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = -3507466186902317988L;
|
||||
private static final String INIT_PARAM_VERSION = "version";
|
||||
private static final String INIT_PARAM_CACHE_PROVIDER = "cacheProvider";
|
||||
private static final String PARAM_IDENTICON_SIZE_SHORT = "s";
|
||||
private static final String PARAM_IDENTICON_CODE_SHORT = "c";
|
||||
private static final String IDENTICON_IMAGE_FORMAT = "PNG";
|
||||
private static final String IDENTICON_IMAGE_MIMETYPE = "image/png";
|
||||
private static final long DEFAULT_IDENTICON_EXPIRES_IN_MILLIS = 24 * 60 * 60 * 1000;
|
||||
private int version = 1;
|
||||
private IdenticonCache cache;
|
||||
private long identiconExpiresInMillis = DEFAULT_IDENTICON_EXPIRES_IN_MILLIS;
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig cfg) throws ServletException {
|
||||
super.init(cfg);
|
||||
|
||||
// Since identicons cache expiration is very long, version is
|
||||
// used in ETag to force identicons to be updated as needed.
|
||||
// Change veresion whenever rendering codes changes result in
|
||||
// visual changes.
|
||||
if (cfg.getInitParameter(INIT_PARAM_VERSION) != null)
|
||||
this.version = Integer.parseInt(cfg
|
||||
.getInitParameter(INIT_PARAM_VERSION));
|
||||
|
||||
String cacheProvider = cfg.getInitParameter(INIT_PARAM_CACHE_PROVIDER);
|
||||
if (cacheProvider != null) {
|
||||
try {
|
||||
Class cacheClass = Class.forName(cacheProvider);
|
||||
this.cache = (IdenticonCache) cacheClass.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException {
|
||||
|
||||
String codeParam = request.getParameter(PARAM_IDENTICON_CODE_SHORT);
|
||||
boolean codeSpecified = codeParam != null && codeParam.length() > 0;
|
||||
if (!codeSpecified) {
|
||||
// TODO 404
|
||||
codeParam="http://stats.i2p/?i2paddresshelper=Okd5sN9hFWx-sr0HH8EFaxkeIMi6PC5eGTcjM1KB7uQ0ffCUJ2nVKzcsKZFHQc7pLONjOs2LmG5H-2SheVH504EfLZnoB7vxoamhOMENnDABkIRGGoRisc5AcJXQ759LraLRdiGSR0WTHQ0O1TU0hAz7vAv3SOaDp9OwNDr9u902qFzzTKjUTG5vMTayjTkLo2kOwi6NVchDeEj9M7mjj5ySgySbD48QpzBgcqw1R27oIoHQmjgbtbmV2sBL-2Tpyh3lRe1Vip0-K0Sf4D-Zv78MzSh8ibdxNcZACmZiVODpgMj2ejWJHxAEz41RsfBpazPV0d38Mfg4wzaS95R5hBBo6SdAM4h5vcZ5ESRiheLxJbW0vBpLRd4mNvtKOrcEtyCvtvsP3FpA-6IKVswyZpHgr3wn6ndDHiVCiLAQZws4MsIUE1nkfxKpKtAnFZtPrrB8eh7QO9CkH2JBhj7bG0ED6mV5~X5iqi52UpsZ8gnjZTgyG5pOF8RcFrk86kHxAAAA";
|
||||
//response.setStatus(403);
|
||||
//return;
|
||||
}
|
||||
|
||||
String sizeParam = request.getParameter(PARAM_IDENTICON_SIZE_SHORT);
|
||||
// very rougly, number of "modules" is about 4 * sqrt(chars)
|
||||
// (assuming 7 bit) default margin each side is 4
|
||||
// assuming level L
|
||||
// min modules is 21x21
|
||||
// shoot for 2 pixels per module
|
||||
int size = Math.max(50, (2 * 4) + (int) (2 * 5 * Math.sqrt(codeParam.length())));
|
||||
if (sizeParam != null) {
|
||||
try {
|
||||
size = Integer.parseInt(sizeParam);
|
||||
if (size < 40)
|
||||
size = 40;
|
||||
else if (size > 512)
|
||||
size = 512;
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
String identiconETag = IdenticonUtil.getIdenticonETag(codeParam.hashCode(), size,
|
||||
version);
|
||||
String requestETag = request.getHeader("If-None-Match");
|
||||
|
||||
if (requestETag != null && requestETag.equals(identiconETag)) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
} else {
|
||||
byte[] imageBytes = null;
|
||||
|
||||
// retrieve image bytes from either cache or renderer
|
||||
if (cache == null
|
||||
|| (imageBytes = cache.get(identiconETag)) == null) {
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
QRCodeWriter qrcw = new QRCodeWriter();
|
||||
BitMatrix matrix;
|
||||
try {
|
||||
matrix = qrcw.encode(codeParam, BarcodeFormat.QR_CODE, size, size);
|
||||
} catch (WriterException we) {
|
||||
throw new IOException("encode failed", we);
|
||||
}
|
||||
MatrixToImageWriter.writeToStream(matrix, IDENTICON_IMAGE_FORMAT, byteOut);
|
||||
imageBytes = byteOut.toByteArray();
|
||||
if (cache != null)
|
||||
cache.add(identiconETag, imageBytes);
|
||||
} else {
|
||||
response.setStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
// set ETag and, if code was provided, Expires header
|
||||
response.setHeader("ETag", identiconETag);
|
||||
if (codeSpecified) {
|
||||
long expires = System.currentTimeMillis()
|
||||
+ identiconExpiresInMillis;
|
||||
response.addDateHeader("Expires", expires);
|
||||
}
|
||||
|
||||
// return image bytes to requester
|
||||
response.setContentType(IDENTICON_IMAGE_MIMETYPE);
|
||||
response.setContentLength(imageBytes.length);
|
||||
response.getOutputStream().write(imageBytes);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
version="3.0">
|
||||
<display-name>WebAppExample - Identicon</display-name>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>net.i2p.imagegen.IdenticonServlet</servlet-name>
|
||||
<servlet-class>net.i2p.imagegen.IdenticonServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>net.i2p.imagegen.QRServlet</servlet-name>
|
||||
<servlet-class>net.i2p.imagegen.QRServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>net.i2p.imagegen.RandomArtServlet</servlet-name>
|
||||
<servlet-class>net.i2p.imagegen.RandomArtServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<!-- precompiled servlets -->
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.imagegen.IdenticonServlet</servlet-name>
|
||||
<url-pattern>/id</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.imagegen.QRServlet</servlet-name>
|
||||
<url-pattern>/qr</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.imagegen.RandomArtServlet</servlet-name>
|
||||
<url-pattern>/ra</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- this webapp doesn't actually use sessions or cookies -->
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
<cookie-config>
|
||||
<http-only>true</http-only>
|
||||
</cookie-config>
|
||||
</session-config>
|
||||
|
||||
</web-app>
|
23
apps/imagegen/imagegen/webapp/src/main/webapp/index.html
Normal file
23
apps/imagegen/imagegen/webapp/src/main/webapp/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Identicon Canvas Test</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<h2>Server-side ID</h2>
|
||||
<img src="id?c=-2044886870&s=15" width=15 height=15>
|
||||
<img src="id?c=-2044886870&s=21" width=21 height=21>
|
||||
<img src="id?c=-2044886870&s=30" width=30 height=30>
|
||||
<img src="id?c=-2044886870&s=48" width=48 height=48>
|
||||
<img src="id?c=-2044886870&s=64" width=64 height=64>
|
||||
<img src="id?c=-2044886870&s=128" width=128 height=128>
|
||||
|
||||
<h2>Server-side QR</h2>
|
||||
<img src="qr?c=https%3a%2f%2fgeti2p.net%2f&s=128" width=128 height=128>
|
||||
<img src="qr?c=https%3a%2f%2fgeti2p.net%2f&s=128" width=128 height=128>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user