Create image captcha

Posted seliote

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Create image captcha相关的知识,希望对你有一定的参考价值。

Like this:

技术图片

package com.seliote.smsbomber.service.impl;

import com.seliote.smsbomber.pojo.soo.captcha.CaptchaSoo;
import com.seliote.smsbomber.service.CaptchaService;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.util.Random;

/**
 * CaptchaService implementation
 *
 * @author seliote
 * @since 2020-07-06
 */
@Service
public class CaptchaServiceImpl implements CaptchaService {

    // Image width = textLength * widthFactor
    private final int widthFactor = 2;

    // Image height = fontSize * heightFactor
    @SuppressWarnings("FieldCanBeLocal")
    private final float heightFactor = 2F;

    // Captcha text length
    private final int textLength = 4;

    // Interference line count = text length * interferenceLineCountFactor
    @SuppressWarnings("FieldCanBeLocal")
    private final int interferenceLineCountFactor = 10;

    // Interference line width = image height * interferenceLineWidthFactor
    @SuppressWarnings("FieldCanBeLocal")
    private final float interferenceLineWidthFactor = 0.03F;

    // Text rotate angle
    @SuppressWarnings("FieldCanBeLocal")
    private final int textAngle = 45;

    // Captcha text range
    private final char[] textRange = "23456789qazwsxedcrfvtgbyhnujmikpQAZWSXEDCRFVTGBYHNUJMKLP" .toCharArray();

    // Captcha text color
    private final Color[] textColor = {Color.WHITE, Color.BLACK, Color.GREEN, Color.ORANGE,
            Color.YELLOW, Color.RED, Color.PINK, Color.BLUE};

    // Use for get random number
    private final Random random = new Random(Instant.now().getNano());

    @Override
    public CaptchaSoo genCaptcha(int size) throws IOException {
        String text = randomText();
        BufferedImage bufferedImage = createImage(size);
        Graphics2D graphics2D = createGraphics2D(bufferedImage);
        fillImage(bufferedImage, graphics2D);
        drawText(text, bufferedImage, graphics2D, size);
        drawInterferenceLines(bufferedImage, graphics2D);
        disposeGraphics2D(graphics2D);
        return new CaptchaSoo(text, img2Bytes(bufferedImage));
    }

    /**
     * Create an empty image
     *
     * @param size Font size, use to calculate image size
     * @return BufferedImage object
     */
    private BufferedImage createImage(int size) {
        final int width = Math.round(size * textLength) + size * widthFactor;
        final int height = Math.round(size * heightFactor);
        return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    }

    /**
     * Create Graphics2D object
     *
     * @param bufferedImage BufferedImage object which want get Graphics2D
     * @return Graphics2D object
     */
    private Graphics2D createGraphics2D(BufferedImage bufferedImage) {
        return bufferedImage.createGraphics();
    }

    /**
     * Dispose Graphics2D object
     *
     * @param graphics2D Graphics2D object which want to dispose
     */
    private void disposeGraphics2D(Graphics2D graphics2D) {
        graphics2D.dispose();
    }

    /**
     * Fill image background
     *
     * @param bufferedImage BufferedImage object
     * @param graphics2D    Graphics2D object
     */
    private void fillImage(BufferedImage bufferedImage, Graphics2D graphics2D) {
        graphics2D.setColor(Color.DARK_GRAY);
        graphics2D.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
    }

    /**
     * Convert a image to byte array
     *
     * @param bufferedImage BufferedImage object
     * @return Byte array from BufferedImage object
     * @throws IOException When convert occur exception
     */
    private byte[] img2Bytes(BufferedImage bufferedImage) throws IOException {
        final String imageType = "JPEG";
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, imageType, byteArrayOutputStream);
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * Create a random text for captcha
     *
     * @return Random text
     */
    private String randomText() {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < textLength; ++i) {
            stringBuilder.append(textRange[random.nextInt(textRange.length)]);
        }
        return stringBuilder.toString();
    }

    /**
     * Draw text for captcha
     *
     * @param text          Text want to draw
     * @param bufferedImage BufferedImage object
     * @param graphics2D    Graphics2D object use to draw
     * @param size          Text font size
     */
    private void drawText(String text, BufferedImage bufferedImage, Graphics2D graphics2D, int size) {
        Assert.isTrue(text.length() == textLength, "Text length incorrect");
        double angle2Radian = Math.PI / 180;
        int imageWidth = bufferedImage.getWidth();
        int imageHeight = bufferedImage.getHeight();
        int eachCharWidth = imageWidth / textLength;
        for (int i = 0; i < textLength; ++i) {
            graphics2D.setColor(textColor[random.nextInt(textColor.length)]);
            graphics2D.setFont(new Font(null, random.nextInt(2) == 0 ? Font.ITALIC : Font.BOLD, size));
            int xPoint = i * eachCharWidth + Math.max((imageWidth - size * textLength), 0) / (widthFactor * 2);
            int yPoint = Math.round((imageHeight - size) / (heightFactor * 2)) + size;
            graphics2D.translate(xPoint, yPoint);
            int angle = random.nextInt(textAngle * 2) - textAngle;
            double radian = angle * angle2Radian;
            graphics2D.rotate(radian);
            graphics2D.drawString(text.substring(i, i + 1), 0, 0);
            graphics2D.rotate(-radian);
            graphics2D.translate(-xPoint, -yPoint);
        }
    }

    /**
     * Draw interference lines for captcha
     *
     * @param bufferedImage BufferImage object
     * @param graphics2D    Graphics2D object
     */
    private void drawInterferenceLines(BufferedImage bufferedImage, Graphics2D graphics2D) {
        int interferenceLineCount = textLength * interferenceLineCountFactor;
        int interferenceLineWidth = Math.round(bufferedImage.getHeight() * interferenceLineWidthFactor);
        int width = bufferedImage.getWidth();
        int height = bufferedImage.getHeight();
        for (int i = 0; i < interferenceLineCount; ++i) {
            graphics2D.setColor(textColor[random.nextInt(textColor.length)]);
            graphics2D.setStroke(new BasicStroke(random.nextInt(interferenceLineWidth)));
            graphics2D.drawLine(random.nextInt(width), random.nextInt(height),
                    random.nextInt(width), random.nextInt(height));
        }
    }
}

Test:

package com.seliote.smsbomber.service.impl;

import com.seliote.smsbomber.config.AppContext;
import com.seliote.smsbomber.config.RootContext;
import com.seliote.smsbomber.pojo.soo.captcha.CaptchaSoo;
import com.seliote.smsbomber.service.CaptchaService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.Instant;

/**
 * CaptchaServiceImpl test
 *
 * @author seliote
 * @since 2020-07-05
 */
@DisplayName("CaptchaServiceImpl")
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = {RootContext.class, AppContext.class})
class CaptchaServiceImplTest {

    private CaptchaService captchaService;

    @Autowired
    public void setCaptchaService(CaptchaService captchaService) {
        this.captchaService = captchaService;
    }

    @DisplayName("genCaptcha")
    @Test
    void genCaptcha() throws IOException {
        for (int i = 0; i < 10; ++i) {
            CaptchaSoo captchaSoo = captchaService.genCaptcha(100);
            try (FileOutputStream fileOutputStream = new FileOutputStream(
                    new File(String.format("/home/seliote/pictures/%s.jpeg", Instant.now().getNano())))) {
                fileOutputStream.write(captchaSoo.getImage());
            }
        }
    }
}

以上是关于Create image captcha的主要内容,如果未能解决你的问题,请参考以下文章

PHP Captcha Imagettftext无法正常工作

小工具 ——快速生成验证码

如何使用 Canvas.create_image

验证码

片段对话框对活动的松散引用

阿里云 windows 2008 使用Thinkphp5 captcha验证码不显示问题