in blocks/cocoon-captcha/cocoon-captcha-impl/src/main/java/org/apache/cocoon/reading/CaptchaReader.java [155:322]
public void generate()
throws IOException {
/* Retrieve the current operational parameters from Cocoon's sitemap */
final int width = this.parameters.getParameterAsInteger("width", 100);
final int height = this.parameters.getParameterAsInteger("height", 50);
Color background = this.getColor("background", Color.white);
Color foreground = this.getColor("foreground", null);
if (foreground == null) {
int r = (RANDOM.nextInt(64) + 96 + background.getRed()) & 0x0ff;
int g = (RANDOM.nextInt(64) + 96 + background.getGreen()) & 0x0ff;
int b = (RANDOM.nextInt(64) + 96 + background.getBlue()) & 0x0ff;
foreground = new Color(r, g, b);
}
final String fontName = this.parameters.getParameter("font", "serif");
final int scale = this.parameters.getParameterAsInteger("scale", 5);
final float amount = this.parameters.getParameterAsFloat("amount", 2);
final float quality = this.parameters.getParameterAsFloat("quality", 0.75F);
final String text = this.source;
/* Create the final buffered image we will be writing to at the bottom */
final BufferedImage result = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
/* Starting with a size of 100, evaluate how big the real font should be */
final Font baseFont = new Font(fontName, Font.PLAIN, 100);
final Graphics2D graphics = this.antialiasedGraphics(result);
final FontMetrics metrics = graphics.getFontMetrics(baseFont);
final Rectangle2D tempSize = metrics.getStringBounds(text, graphics);
/* Evaluate the image size of the resulting image and prepare a ratio */
final double tempWidth = tempSize.getWidth() + (2 * tempSize.getHeight());
final double tempHeight = (tempSize.getHeight() * (1 + amount));
final double ratioWidth = width * scale / tempWidth;
final double ratioHeight = height * scale / tempHeight;
final double ratio = ratioWidth < ratioHeight? ratioWidth: ratioHeight;
final Font font = baseFont.deriveFont((float) (100 * ratio));
/* Evaluate the final size of the text to write */
final FontMetrics sourceMetrics = graphics.getFontMetrics(font);
final Rectangle2D size = sourceMetrics.getStringBounds(text, graphics);
final double textWidth = size.getWidth();
final double textHeight = size.getHeight();
/* Evaluate the final size of the interim images */
int scaledWidth = (int) (tempWidth * ratio);
int scaledHeight = (int) (tempHeight * ratio);
/* Create a couple of images to write the plain string and the warped one */
BufferedImage source = new BufferedImage(scaledWidth, scaledHeight,
BufferedImage.TYPE_BYTE_GRAY);
BufferedImage warped = new BufferedImage(scaledWidth, scaledHeight,
BufferedImage.TYPE_INT_ARGB);
/* Prepare the background and the font of the source image */
final Graphics2D sourceGraphics = this.antialiasedGraphics(source);
sourceGraphics.setColor(Color.black);
sourceGraphics.fillRect(0, 0, scaledWidth, scaledHeight);
sourceGraphics.setFont(font);
/* Write the string exactly in the middle of the source image */
float textX = (float) ((scaledWidth - textWidth) / 2);
float textY = (float) ((scaledHeight - textHeight) / 2);
sourceGraphics.setColor(Color.white);
sourceGraphics.drawString(text, textX, textY + sourceMetrics.getAscent());
/* Randomize displacement factors for sine-waves */
final int displaceTop = RANDOM.nextInt(scaledWidth);
final int displaceBtm = RANDOM.nextInt(scaledWidth);
final int displaceVer = RANDOM.nextInt(scaledHeight);
/* Calculate the horizontal and vertical amplitude and wavelength of sines */
final double amplitHor = textHeight * amount / 4;
final double amplitVer = textHeight / 8;
final double t = (RANDOM.nextDouble() * textWidth / 2) + (textWidth * 0.75);
final double b = (RANDOM.nextDouble() * textWidth / 2) + (textWidth * 0.75);
final double wlenTop = textHeight > t? textHeight: t;
final double wlenBtm = textHeight > b? textHeight: b;
/* Calculate the offsets for horizontal (top and bottom) sine waves */
final double offsetTop = amplitHor;
final double offsetBtm = scaledHeight - amplitHor;
/* Prepare an array for vertical displacement sine wave */
final double vert[] = new double[scaledHeight];
for (int v = 0; v < scaledHeight ; v++) {
vert[v] = Math.sin((Math.PI * (v + displaceVer)) / textHeight) * amplitVer;
}
/* Iterate all the target image pixels and render the distortion */
int x1 = Integer.MAX_VALUE;
int x2 = Integer.MIN_VALUE;
int y1 = Integer.MAX_VALUE;
int y2 = Integer.MIN_VALUE;
final WritableRaster sourceRaster = source.getRaster();
final WritableRaster warpedRaster = warped.getRaster();
final double src[] = new double[9];
final double col[] = new double[] { foreground.getRed(),
foreground.getGreen(),
foreground.getBlue(), 0};
for (int h = 0; h < scaledWidth; h++) {
final double baseTop = (Math.PI * (h + displaceTop)) / wlenTop;
final double baseBtm = (Math.PI * (h + displaceBtm)) / wlenBtm;
final double top = offsetTop + Math.sin(baseTop) * amplitHor;
final double btm = offsetBtm - Math.sin(baseBtm) * amplitHor;
for (int v = 0; v < scaledHeight; v ++) {
final double x = (h + vert[v]);
final double y = (v * ((btm - top) / scaledHeight)) + top;
if ((y > 0) && (y < scaledHeight - 1) &&
(x > 0) && (x < scaledWidth - 1)) {
/* Retrieve the nine pixels around the source one */
sourceRaster.getPixels((int)(x-1), (int)(y-1), 3, 3, src);
/* Average their value (it's grayscale) to have a better warp */
double alpha = ((src[1] + src[3] + src[5] + src[7]) * 0.1) +
((src[0] + src[2] + src[6] + src[8]) * 0.025) +
(src[4] * 0.5);
/* Write the resultin pixel in the target image if necessary */
if (alpha > 0) {
col[3] = alpha;
warpedRaster.setPixel(h, v, col);
if (h < x1) x1 = h;
if (h > x2) x2 = h;
if (v < y1) y1 = v;
if (v > y2) y2 = v;
}
}
}
}
/* Crop the image to the maximum extent of the warped text (if visible) */
source = null;
int xd = x2 - x1 + 1;
int yd = y2 - y1 + 1;
if ((xd > 1) && (yd > 1)) {
warped = warped.getSubimage(x1, y1, xd, yd);
}
/* Rescale the cropped image to the required size */
Image image = warped.getScaledInstance(width, height, Image.SCALE_SMOOTH);
graphics.setBackground(background);
graphics.setColor(background);
graphics.fillRect(0, 0, width, height);
graphics.setColor(foreground);
graphics.drawImage(image, 0, 0, null);
/* Write the processed image as a JPEG image */
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(quality);
jpgWriter.setOutput(buffer);
IIOImage outputImage = new IIOImage((BufferedImage) image, null, null);
jpgWriter.write(null, outputImage, jpgWriteParam);
jpgWriter.dispose();
buffer.flush();
buffer.close();
this.out.write(buffer.toByteArray());
this.out.flush();
}