package railo.commons.img; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; /** * Abstract template class for captcha generation */ public abstract class AbstractCaptcha { public static final int DIFFICULTY_LOW=0; public static final int DIFFICULTY_MEDIUM=1; public static final int DIFFICULTY_HIGH=2; /** * generates a Captcha as a Buffered Image file * @param text text for the captcha * @param width width of the resulting image * @param height height of the resulting image * @param fonts list of font used for the captcha (all font are random used) * @param useAntiAlias use anti aliasing or not * @param fontColor color of the font * @param fontSize size of the font * @param difficulty difficulty of the reslting captcha * @return captcha image * @throws CaptchaException */ public BufferedImage generate(String text,int width, int height, String[] fonts, boolean useAntiAlias, Color fontColor,int fontSize, int difficulty) throws CaptchaException { if(difficulty==DIFFICULTY_LOW) { return generate(text, width, height, fonts, useAntiAlias, fontColor,fontSize, 0, 0, 0, 0, 0, 0,230,25); } if(difficulty==DIFFICULTY_MEDIUM) { return generate(text, width, height, fonts, useAntiAlias, fontColor,fontSize, 0, 0, 5, 30, 0, 0,200,35); } return generate(text, width, height, fonts, useAntiAlias, fontColor,fontSize, 4, 10, 30, 60, 4, 10,170,45); } private BufferedImage generate(String text,int width, int height, String[] fonts, boolean useAntiAlias , Color fontColor,int fontSize , int minOvals, int maxOvals, int minBGLines, int maxBGLines, int minFGLines, int maxFGLines, int startColor, int shear) throws CaptchaException { if(text==null || text.trim().length()==0) throw new CaptchaException("missing Text"); char[] characters=text.toCharArray(); int top=height/3; Dimension dimension = new Dimension(width, height); int imageType = BufferedImage.TYPE_INT_RGB; BufferedImage bufferedImage = new BufferedImage((int)dimension.getWidth(), (int)dimension.getHeight(), imageType); Graphics2D graphics = bufferedImage.createGraphics(); // Set anti-alias setting if(useAntiAlias) graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawBackground(graphics, dimension,startColor); // draw ovals if(maxOvals>0 && maxOvals>minOvals) { int to=rnd(minOvals,maxOvals); for(int i=1;i<=to;i++) { drawRandomOval(graphics, dimension, getRandomColor(startColor)); } } // Draw background lines if(maxBGLines>0 && maxBGLines>minBGLines) { int to=rnd(minBGLines,maxBGLines); for(int i=1;i<=to;i++) { drawRandomLine(graphics, dimension, getRandomColor(startColor)); } } if(fonts==null || fonts.length==0) throw new CaptchaException("no font's defined"); // font Font f; ArrayList fontList=new ArrayList(); for(int i=0;i<fonts.length;i++){ f=getFont(fonts[i],null); if(f!=null) fontList.add(f); } if(fonts.length==0) throw new CaptchaException("defined fonts are not available on this system"); int charWidth=0; int charHeight=0,tmp; int space=0; Font[] _fonts=new Font[characters.length]; for(int i=0;i<characters.length;i++) { char c= characters[i]; _fonts[i]=createFont(fontList,fontSize,shear,i); graphics.setFont(_fonts[i]); charWidth+=graphics.getFontMetrics().charWidth(c); tmp=graphics.getFontMetrics().getHeight(); if(tmp>charHeight)charHeight=tmp; } if(charWidth<width) { space=(width-charWidth)/(characters.length+1); } else if (charWidth>width)throw new CaptchaException("the specified width for the CAPTCHA image is not big enough to fit the text. Minimum width is ["+charWidth+"]"); if (charHeight>height)throw new CaptchaException("the specified height for the CAPTCHA image is not big enough to fit the text. Minimum height is ["+charHeight+"]"); int left= space; // Draw captcha text for(int i=0;i<characters.length;i++) { char c= characters[i]; // <cfset staticCollections.shuffle(definedFonts) /> graphics.setFont(_fonts[i]); graphics.setColor(fontColor); // Check if font can display current character ---> /*<cfloop condition="NOT graphics.getFont().canDisplay(char)"> <cfset setFont(graphics, definedFonts) /> </cfloop>*/ // Compute the top character position ---> top = rnd(graphics.getFontMetrics().getAscent(), height - (height - graphics.getFontMetrics().getHeight()) / 2); // Draw character text graphics.drawString(String.valueOf(c),left,top); // Compute the next character lef tposition ---> //((rnd(150, 200) / 100) * left += graphics.getFontMetrics().charWidth(c)+rnd(space, space); } // Draw forground lines if(maxFGLines>0 && maxFGLines>minFGLines) { int to=rnd(minFGLines,maxFGLines); for(int i=1;i<=to;i++) { drawRandomLine(graphics, dimension, getRandomColor(startColor)); } } return bufferedImage; } /** * creates a font from given string * @param font * @param defaultValue * @return */ public abstract Font getFont(String font, Font defaultValue); private void drawBackground(Graphics2D graphics, Dimension dimension,int _startColor) { Color startColor = getRandomColor(_startColor); Color endColor = getRandomColor(_startColor); GradientPaint gradientPaint = new GradientPaint(getRandomPointOnBorder(dimension), startColor, getRandomPointOnBorder(dimension), endColor.brighter(), true); graphics.setPaint(gradientPaint); // arguments.graphics.setColor(startColor) /> graphics.fill(new Rectangle(dimension)); } private Font createFont(List fonts, int fontSize, int shear,int index) { AffineTransform trans1 = getRandomTransformation(shear, shear); AffineTransform trans2 = getRandomTransformation(shear, shear); Font font = (Font) fonts.get(index%fonts.size()); font = font.deriveFont((float)fontSize).deriveFont(trans1).deriveFont(trans2); return font; } private Color getRandomColor(int startColor) { return new Color(r(startColor),r(startColor),r(startColor)); } private int r(int startColor) { return rnd(startColor-100,startColor); //int r= ((int)(Math.random()*(255-startColor)))+startColor; //return r; } private Point getRandomPointOnBorder(Dimension dimension) { int height = (int) dimension.getHeight(); int width = (int) dimension.getWidth(); switch(rnd(1, 4)) { case 1: // left side return new Point(0, rnd(0, height)); case 2: // right side return new Point(width, rnd(0, height)); case 3: // top side return new Point(rnd(0, width), 0); case 4: default: // bottom side return new Point(rnd(0, width), height); } } private AffineTransform getRandomTransformation(int shearXRange, int shearYRange) { // create a slightly random affine transform double shearX = rndd(-1 * (shearXRange * (rnd(50, 150) / 100d)), (shearXRange* (rndd(50, 150) / 100d))) / 100d ; double shearY = rndd(-1 * (shearYRange * (rnd(50, 150) / 100d)), (shearYRange * (rndd(50, 150) / 100d))) / 100d ; AffineTransform transformation = new AffineTransform(); transformation.shear(shearX, shearY); return transformation; } private BasicStroke getRandomStroke() { return new BasicStroke(rnd(1, 3)); } private Point getRandomPoint(Dimension dimension) { int height = (int) dimension.getHeight(); int width = (int) dimension.getWidth(); return new Point(rnd(0, width), rnd(0, height)); } protected static int rnd(double min, double max) { return (int) rndd(min, max); } private static double rndd(double min, double max) { if(min>max) { double tmp=min; min=max; max=tmp; } double diff=max-min; return ((int)(StrictMath.random()*(diff+1)))+min; } private void drawRandomLine(Graphics2D graphics, Dimension dimension, Color lineColorType) { Point point1 = getRandomPointOnBorder(dimension); Point point2 = getRandomPointOnBorder(dimension); graphics.setStroke(getRandomStroke()); graphics.setColor(lineColorType); graphics.drawLine((int)point1.getX(), (int)point1.getY(), (int)point2.getX(), (int)point2.getY()); } private void drawRandomOval(Graphics2D graphics, Dimension dimension, Color ovalColorType) { Point point = getRandomPoint(dimension); double height = dimension.getHeight() ; //double width = dimension.getWidth() ; double minOval = height * .10; double maxOval = height * .75; graphics.setColor(ovalColorType); switch(rnd(1, 3)) { case 1: graphics.setStroke(getRandomStroke()); graphics.drawOval( (int)point.getX(), (int)point.getY(), rnd(minOval,maxOval), rnd(minOval,maxOval) ); break; case 2: case 3: graphics.fillOval( (int)point.getX(), (int)point.getY(), rnd(minOval,maxOval), rnd(minOval,maxOval) ); break; } } }