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

org.bardframework.commons.captcha.CaptchaService Maven / Gradle / Ivy

The newest version!
package org.bardframework.commons.captcha;

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.bardframework.commons.utils.persian.LetterConverterUtility;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

@Setter
@Getter
public class CaptchaService {

    private final Random random = new Random();
    private Map captchaTypeInfoMap = new ConcurrentHashMap<>();

    public CaptchaService() {
        captchaTypeInfoMap.put(CaptchaType.PERSIAN_NUMBER_TEXT, new CaptchaTypeInfo("0123456789", false, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.ENGLISH_NUMBER, new CaptchaTypeInfo("0123456789", false, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.PERSIAN_CHAR, new CaptchaTypeInfo("بتجحخدرژسشضظعفقکلمنوهی", true, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.PERSIAN_NUMBER_CHAR, new CaptchaTypeInfo("0123456789بتجحخدرژسشضظعفقکلمنوهی", true, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.ARABIC_CHAR, new CaptchaTypeInfo("ابتثجحخدذرزسشصضطظفقعغکلمنوهی", true, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.ARABIC_NUMBER_CHAR, new CaptchaTypeInfo("0123456789ابتثجحخدذرزسشصضطظفقعغکلمنوهی", true, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.ENGLISH_CHAR, new CaptchaTypeInfo("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", false, new Font("Tahoma", Font.PLAIN, 40)));
        captchaTypeInfoMap.put(CaptchaType.ENGLISH_NUMBER_CHAR, new CaptchaTypeInfo("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", false, new Font("Tahoma", Font.PLAIN, 40)));
    }

    public GeneratedCaptcha generateCaptcha(CaptchaType type, int maxChars) {
        CaptchaTypeInfo captchaTypeInfo = this.getCaptchaTypeInfoMap().get(type);
        if (null == captchaTypeInfo) {
            throw new IllegalStateException(String.format("no chars specify for type %s to generate captcha", type));
        }
        StringBuilder captchaText;
        if (StringUtils.isNumeric(captchaTypeInfo.getChars())) {
            captchaText = new StringBuilder(RandomStringUtils.random(maxChars, captchaTypeInfo.getChars()));
        } else {
            captchaText = this.removeContinuousNumbers(RandomStringUtils.random(maxChars, captchaTypeInfo.getChars()), captchaTypeInfo.getChars());
        }
        String text = captchaText.toString();
        String textForImage;
        if (type == CaptchaType.PERSIAN_NUMBER_TEXT) {
            textForImage = LetterConverterUtility.convertDigitToFarsiLetter(Long.valueOf(text));
        } else {
            textForImage = captchaTypeInfo.isRtl() ? captchaText.reverse().toString() : captchaText.toString();
        }
        return new GeneratedCaptcha(text, this.generateImage(type, textForImage, captchaTypeInfo.getFont()));
    }

    /**
     * Generates a PNG image of text 180 pixels wide, 40 pixels high with white background.
     *
     * @param text expects string size eight (8) characters.
     * @return byte array that is a PNG image generated with text displayed.
     */
    private byte[] generateImage(CaptchaType type, String text, Font font) {
        int height = 40;
        int width = Math.min(text.length() * height, 400);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = image.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        graphics.setColor(new Color(RandomUtils.nextInt(170, 255), RandomUtils.nextInt(180, 255), RandomUtils.nextInt(200, 255)));
        graphics.fillRect(0, 0, width, height);

        graphics.setFont(font);
        graphics.setColor(this.getRandomColor());
        int start = 30;
        if (type != CaptchaType.PERSIAN_NUMBER_TEXT) {
            for (int i = 0; i < text.length(); i++) {
                graphics.drawString(text.substring(i, i + 1), start + (i * 20), random.nextInt(15) + 25);
            }
        } else {
            String[] parts = text.split(StringUtils.SPACE);
            int j = 0;
            for (int i = parts.length - 1; i >= 0; i--) {
                graphics.drawString(parts[i], start + parts[i].length() + (j * 82), random.nextInt(15) + 25);
                ++j;
            }
        }
        for (int i = 0; i < text.length() - 2; i++) {
            graphics.drawOval(random.nextInt(width), random.nextInt(height), random.nextInt(500), random.nextInt(100));
        }
        graphics.dispose();
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            ImageIO.write(image, "png", outputStream);
            return outputStream.toByteArray();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * remove numbers from first, last amd continuous
     */
    private StringBuilder removeContinuousNumbers(String text, String validChars) {
        StringBuilder result = new StringBuilder();
        boolean previousNumber = false;
        for (int i = 0; i < text.length(); i++) {
            if (Character.isDigit(text.charAt(i)) && (i == 0 || i == text.length() - 1 || previousNumber)) {
                String notDigit = RandomStringUtils.random(1, validChars);
                while (Character.isDigit(notDigit.charAt(0))) {
                    notDigit = RandomStringUtils.random(1, validChars);
                }
                result.append(notDigit);
                previousNumber = false;
            } else {
                result.append(text.charAt(i));
                previousNumber = Character.isDigit(text.charAt(i));
            }
        }
        return result;
    }

    private Color getRandomColor() {
        return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy