Package org.apache.wicket.extensions.markup.html.captcha

Source Code of org.apache.wicket.extensions.markup.html.captcha.CaptchaImageResource$CharAttributes

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.wicket.extensions.markup.html.captcha;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import org.apache.wicket.IClusterable;
import org.apache.wicket.markup.html.image.resource.DynamicImageResource;
import org.apache.wicket.util.time.Time;


/**
* Generates a captcha image.
*
* @author Joshua Perlow
*/
public final class CaptchaImageResource extends DynamicImageResource
{
  /**
   * This class is used to encapsulate all the filters that a character will
   * get when rendered. The changes are kept so that the size of the shapes
   * can be properly recorded and reproduced later, since it dynamically
   * generates the size of the captcha image. The reason I did it this way is
   * because none of the JFC graphics classes are serializable, so they cannot
   * be instance variables here. If anyone knows a better way to do this,
   * please let me know.
   */
  private static final class CharAttributes implements IClusterable
  {
    private static final long serialVersionUID = 1L;
    private char c;
    private String name;
    private int rise;
    private double rotation;
    private double shearX;
    private double shearY;

    CharAttributes(char c, String name, double rotation, int rise, double shearX, double shearY)
    {
      this.c = c;
      this.name = name;
      this.rotation = rotation;
      this.rise = rise;
      this.shearX = shearX;
      this.shearY = shearY;
    }

    char getChar()
    {
      return c;
    }

    String getName()
    {
      return name;
    }

    int getRise()
    {
      return rise;
    }

    double getRotation()
    {
      return rotation;
    }

    double getShearX()
    {
      return shearX;
    }

    double getShearY()
    {
      return shearY;
    }
  }

  private static final long serialVersionUID = 1L;

  private static int randomInt(int min, int max)
  {
    return (int)(Math.random() * (max - min) + min);
  }

  private static String randomString(int min, int max)
  {
    int num = randomInt(min, max);
    byte b[] = new byte[num];
    for (int i = 0; i < num; i++)
      b[i] = (byte)randomInt('a', 'z');
    return new String(b);
  }

  private String challengeId;
  private final List charAttsList;

  private List fontNames = Arrays.asList(new String[] { "Helventica", "Arial", "Courier" });
  private final int fontSize;
  private final int fontStyle;

  private int height = 0;

  /** Transient image data so that image only needs to be generated once per VM */
  private transient SoftReference imageData;

  private final int margin;

  private int width = 0;

  /**
   * Construct.
   */
  public CaptchaImageResource()
  {
    this(randomString(6, 8));
  }

  /**
   * Construct.
   *
   * @param challengeId
   *            The id of the challenge
   */
  public CaptchaImageResource(String challengeId)
  {
    this(challengeId, 48, 30);
  }

  /**
   * Construct.
   *
   * @param challengeId
   *            The id of the challenge
   * @param fontSize
   *            The font size
   * @param margin
   *            The image's margin
   */
  public CaptchaImageResource(String challengeId, int fontSize, int margin)
  {
    this.challengeId = challengeId;
    this.fontStyle = 1;
    this.fontSize = fontSize;
    this.margin = margin;
    this.width = this.margin * 2;
    this.height = this.margin * 2;
    char[] chars = challengeId.toCharArray();
    charAttsList = new ArrayList();
    TextLayout text;
    AffineTransform textAt;
    Shape shape;
    for (int i = 0; i < chars.length; i++)
    {
      String fontName = (String)fontNames.get(randomInt(0, fontNames.size()));
      double rotation = Math.toRadians(randomInt(-35, 35));
      int rise = randomInt(margin / 2, margin);
      Random ran = new Random();
      double shearX = ran.nextDouble() * 0.2;
      double shearY = ran.nextDouble() * 0.2;
      CharAttributes cf = new CharAttributes(chars[i], fontName, rotation, rise, shearX,
          shearY);
      charAttsList.add(cf);
      text = new TextLayout(chars[i] + "", getFont(fontName), new FontRenderContext(null,
          false, false));
      textAt = new AffineTransform();
      textAt.rotate(rotation);
      textAt.shear(shearX, shearY);
      shape = text.getOutline(textAt);
      this.width += (int)shape.getBounds2D().getWidth();
      if (this.height < (int)shape.getBounds2D().getHeight() + rise)
      {
        this.height = (int)shape.getBounds2D().getHeight() + rise;
      }
    }
  }

  /**
   * Gets the id for the challenge.
   *
   * @return The the id for the challenge
   */
  public final String getChallengeId()
  {
    return challengeId;
  }

  /**
   * Causes the image to be redrawn the next time its requested.
   *
   * @see org.apache.wicket.Resource#invalidate()
   */
  public final void invalidate()
  {
    imageData = null;
  }

  /**
   * @see org.apache.wicket.markup.html.image.resource.DynamicImageResource#getImageData()
   */
  protected final byte[] getImageData()
  {
    // get image data is always called in sync block
    byte[] data = null;
    if (imageData != null)
    {
      data = (byte[])imageData.get();
    }
    if (data == null)
    {
      data = render();
      imageData = new SoftReference(data);
      setLastModifiedTime(Time.now());
    }
    return data;
  }

  private Font getFont(String fontName)
  {
    return new Font(fontName, fontStyle, fontSize);
  }

  /**
   * Renders this image
   *
   * @return The image data
   */
  private final byte[] render()
  {
    while (true)
    {
      final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      Graphics2D gfx = (Graphics2D)image.getGraphics();
      gfx.setBackground(Color.WHITE);
      int curWidth = margin;
      for (int i = 0; i < charAttsList.size(); i++)
      {
        CharAttributes cf = (CharAttributes)charAttsList.get(i);
        TextLayout text = new TextLayout(cf.getChar() + "", getFont(cf.getName()), gfx
            .getFontRenderContext());
        AffineTransform textAt = new AffineTransform();
        textAt.translate(curWidth, height - cf.getRise());
        textAt.rotate(cf.getRotation());
        textAt.shear(cf.getShearX(), cf.getShearY());
        Shape shape = text.getOutline(textAt);
        curWidth += shape.getBounds().getWidth();
        gfx.setXORMode(Color.BLACK);
        gfx.fill(shape);
      }

      // XOR circle
      int dx = randomInt(width, 2 * width);
      int dy = randomInt(width, 2 * height);
      int x = randomInt(0, width / 2);
      int y = randomInt(0, height / 2);

      gfx.setXORMode(Color.BLACK);
      gfx.setStroke(new BasicStroke(randomInt(fontSize / 8, fontSize / 2)));
      gfx.drawOval(x, y, dx, dy);

      WritableRaster rstr = image.getRaster();
      int[] vColor = new int[3];
      int[] oldColor = new int[3];
      Random vRandom = new Random(System.currentTimeMillis());

      // noise
      for (x = 0; x < width; x++)
      {
        for (y = 0; y < height; y++)
        {
          rstr.getPixel(x, y, oldColor);

          // hard noise
          vColor[0] = 0 + (int)(Math.floor(vRandom.nextFloat() * 1.03) * 255);
          // soft noise
          vColor[0] = vColor[0] ^ (170 + (int)(vRandom.nextFloat() * 80));
          // xor to image
          vColor[0] = vColor[0] ^ oldColor[0];
          vColor[1] = vColor[0];
          vColor[2] = vColor[0];

          rstr.setPixel(x, y, vColor);
        }
      }
      return toImageData(image);
    }
  }
}
TOP

Related Classes of org.apache.wicket.extensions.markup.html.captcha.CaptchaImageResource$CharAttributes

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.