All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zkoss.zkex.zul.impl.JHLabsCaptchaEngine Maven / Gradle / Ivy

There is a newer version: 3.6.3
Show newest version
/* JHLabsCaptchaEngine.java

{{IS_NOTE
	Purpose:

	Description:

	History:
		Tue Aug 1 10:30:48     2006, Created by henrichen
}}IS_NOTE

Copyright (C) 2006 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under GPL Version 2.0 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zkex.zul.impl;

import org.zkoss.zul.*;
import org.zkoss.zul.impl.CaptchaEngine;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.UiException;
import org.zkoss.image.AImage;
import org.zkoss.util.TimeZones;
import org.zkoss.lang.Strings;
import org.zkoss.lang.Objects;

import com.jhlabs.image.RippleFilter;
import com.jhlabs.image.ShadowFilter;

import java.awt.Font;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Graphics2D;
import java.awt.BasicStroke;
import java.awt.GradientPaint;
import java.awt.RenderingHints;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.PathIterator;
import java.awt.geom.AffineTransform;
import java.awt.font.FontRenderContext;

import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.io.ByteArrayOutputStream;

import javax.imageio.ImageIO;

/**
 * A captcha engine implemented with JH Labs libraries.
 *
 * 

See also JH Labs * @author henrichen * @since 3.0.0 */ public class JHLabsCaptchaEngine implements CaptchaEngine, java.io.Serializable { private static Random _random = new Random(); private static final double[] sin = { //sin degree 0 ~ 10 Math.sin(Math.toRadians(0)), Math.sin(Math.toRadians(1)), Math.sin(Math.toRadians(2)), Math.sin(Math.toRadians(3)), Math.sin(Math.toRadians(4)), Math.sin(Math.toRadians(5)), /* Math.sin(Math.toRadians(6)), Math.sin(Math.toRadians(7)), Math.sin(Math.toRadians(8)), Math.sin(Math.toRadians(9)), Math.sin(Math.toRadians(10)), */ }; private static final double[] cos = { //cos degree 0 ~ 10 Math.cos(Math.toRadians(0)), Math.cos(Math.toRadians(1)), Math.cos(Math.toRadians(2)), Math.cos(Math.toRadians(3)), Math.cos(Math.toRadians(4)), Math.cos(Math.toRadians(5)), /* Math.cos(Math.toRadians(6)), Math.cos(Math.toRadians(7)), Math.cos(Math.toRadians(8)), Math.cos(Math.toRadians(9)), Math.cos(Math.toRadians(10)), */ }; //-- CaptchaEngine --// public byte[] generateCaptcha(Object data) { final Captcha captcha = (Captcha) data; final BufferedImage bi = drawCaptcha(captcha); //encode the image to jpeg format final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { if (!ImageIO.write(bi, "png", baos)) throw new UiException("Don't know how to generate PNG"); } catch(java.io.IOException ex) { throw UiException.Aide.wrap(ex); } finally { try { baos.close(); } catch(java.io.IOException ex) { throw UiException.Aide.wrap(ex); } } return baos.toByteArray(); } private BufferedImage drawCaptcha(Captcha captcha) { final int width = captcha.getIntWidth(); final int height = captcha.getIntHeight(); final int bgRGB = captcha.getBgRGB(); final int fontRGB = captcha.getFontRGB(); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); BufferedImage bitgt = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2D = null; Graphics2D gtgt = null; try { g2D = bi.createGraphics(); //draw text final Color fontColor = new Color(fontRGB); g2D.setColor(fontColor); int textwidth = drawText(g2D, captcha); //distortion bi = distortion(bi); //box background final Color bgColor = new Color(bgRGB, false); GradientPaint grbgColor = new GradientPaint(0, 0, bgColor, width, height, Color.WHITE); gtgt = bitgt.createGraphics(); gtgt.setPaint(grbgColor); gtgt.fillRect(0, 0, width, height); int wgap = width - textwidth; int left = wgap <= 0 ? 0 : _random.nextInt(wgap); //draw the distortion image on the box gtgt.drawImage(bi, left, 0, null); //draw the noise if (captcha.isNoise()) { genNoise(bitgt, .1f, .1f, .25f, .25f, fontColor); genNoise(bitgt, .1f, .25f, .5f, .9f, fontColor); } //box border gtgt.setColor(fontColor); gtgt.drawRect(0, 0, width-1, height-1); return bitgt; } finally { if (g2D != null) g2D.dispose(); if (gtgt != null) gtgt.dispose(); } } protected int drawText(Graphics2D g2D, Captcha captcha) { final int width = captcha.getIntWidth(); final int height = captcha.getIntHeight(); final String text = captcha.getValue(); final int len = text.length(); final int fontSize = captcha.getFonts().isEmpty() ? captcha.getDefaultFonts().length : captcha.getFonts().size(); final int bgRGB = captcha.getBgRGB(); final int fontRGB = captcha.getFontRGB(); final Color bgColor = new Color(bgRGB, true); final Color fontColor = new Color(fontRGB); int avgwidth = width / len; int left = 0; int top = 0; int werror = 0; //adjust width error, so all words can be show correctly for(int j = 0; j < len; ++j) { final Font font = captcha.getFont(_random.nextInt(fontSize)); g2D.setFont(font); final String substr = text.substring(j, j+1); final FontRenderContext frc = g2D.getFontRenderContext(); final Rectangle2D r2D = font.getStringBounds(substr, frc); BufferedImage charImg = rotateChar(substr, (int) r2D.getWidth(), (int) r2D.getHeight(), font, fontColor, bgColor); final int charWidth = charImg.getWidth(); final int charHeight = charImg.getHeight(); //gap to be move around final int wgap = avgwidth - charWidth + werror; if (wgap <= 0) { werror = wgap; } final int hgap = height - charHeight; left += (wgap <= 0 ? 0 : _random.nextInt(wgap)); top = (hgap <= 0 ? 0 : _random.nextInt(hgap)); g2D.drawImage(charImg, left, top, bgColor, null); left += charWidth; } return left; } protected BufferedImage rotateChar(String substr, int width, int height, Font font, Color fontColor, Color bgColor) { final int angle = _random.nextInt(11) - 5; //-5 ~ 5 degree final int[] resultxy = newXy(width, height, angle); final int newWidth = resultxy[0]; final int newHeight = resultxy[1]; Graphics2D g2D = null; try { BufferedImage bi = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); g2D = bi.createGraphics(); RenderingHints hints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.add(new RenderingHints( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)); g2D.setRenderingHints(hints); //background g2D.setColor(bgColor); g2D.fillRect(0, 0, newWidth, newHeight); //draw text with rotate transformation g2D.setFont(font); int xRot = newWidth / 2; int yRot = newHeight / 2; AffineTransform xform = g2D.getTransform(); xform.rotate(Math.toRadians(angle), xRot, yRot); g2D.setTransform(xform); g2D.setColor(fontColor); int left = (newWidth-width) / 2; int top = (newHeight-height)/ 2 + (height * 2 / 3); g2D.drawString(substr, left, top); return bi; } finally { if (g2D != null) { g2D.dispose(); } } } private BufferedImage distortion(BufferedImage image) { final int width = image.getWidth(); final int height = image.getHeight(); //RippleFilter RippleFilter rfilter = new RippleFilter(); rfilter.setWaveType(RippleFilter.SINE); //SINE or NOISE rfilter.setXWavelength(_random.nextInt(8)+9); rfilter.setYWavelength(_random.nextInt(3)+2); rfilter.setXAmplitude(5.6f); rfilter.setYAmplitude(_random.nextFloat()+1.0f); image = rfilter.filter(image, null); //ShadowFilter ShadowFilter sfilter = new ShadowFilter(); sfilter.setRadius(height/4); image = sfilter.filter(image, null); return image; } private void genNoise(BufferedImage image, float factorOne,float factorTwo, float factorThree, float factorFour, Color fontColor){ final int width = image.getWidth(); final int height = image.getHeight(); //the curve from where the points are taken CubicCurve2D cc = new CubicCurve2D.Float( width*factorOne*_random.nextFloat(), height*_random.nextFloat(), width*factorTwo, height*_random.nextFloat(), width*factorThree, height*_random.nextFloat(), width*factorFour, height*_random.nextFloat()); // creates an iterator to define the boundary of the flattened curve PathIterator pathIt = cc.getPathIterator(null, 2); List points = new ArrayList(256); // while pathIt is iterating the curve, remember the points. for(; !pathIt.isDone(); pathIt.next()) { float[] coords = new float[6]; switch ( pathIt.currentSegment(coords) ) { case PathIterator.SEG_MOVETO: case PathIterator.SEG_LINETO: points.add(new Point2D.Float(coords[0], coords[1])); } } Graphics2D g2D = null; try { g2D = (Graphics2D)image.getGraphics(); g2D.setRenderingHints(new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); g2D.setColor(fontColor); final int count = points.size() - 1; for(int j = 0; j < count; ++j) { //for the first 3 point change the stroke and direction if(j < 3) { g2D.setStroke(new BasicStroke(0.9f * (3-j))); } Point2D thisPoint = (Point2D) points.get(j); Point2D nextPoint = (Point2D) points.get(j+1); g2D.drawLine((int)thisPoint.getX(), (int) thisPoint.getY(), (int)nextPoint.getX(), (int)nextPoint.getY()); } } finally { if (g2D != null) { g2D.dispose(); } } } /** Given original x and y, and rotating degree, return the new x and y after rotation. */ private int[] newXy(int x, int y, int degree) { //x' = s . cos(-org + degree), x' = x . cos(degree) + y . sin(degree) //y' = s . sin( org + degree), y' = y . cos(degree) + x . sin(degree) //x' = s . cos(-org - degree) = s . cos(org + degree), x' = x . cos(degree) - y . sin(degree) //y' = s . sin( org - degree), y' = y . cos(degree) + x . sin(degree) if (degree < 0) { degree = -degree; x = (int) (x * cos[degree] + y * sin[degree]); y = (int) (y * cos[degree] - x * sin[degree]); } else { x = (int) (x * cos[degree] - y * sin[degree]); y = (int) (y * cos[degree] + x * sin[degree]); } final int[] result = new int[2]; result[0] = x; result[1] = y; return result; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy