解锁新技能《Java绘制2D图形验证码方法》

news2024/9/24 13:17:23
一、常见的图形验证码有如下三种:
  • 字母数字混合(干扰线);
  • 纯数字(干扰线);
  • 纯字母(干扰线);
二、示例图如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、案例代码

问题一:绘制验证码的(x,y)坐标相对的点是哪里?

答案:左上角

问题二:绘制图形验证码的x坐标起始位置计算方法?

答案:(画板宽度-验证码字符文本宽度)/2

问题三:如何逐个计算每个字符绘制的x坐标位置?

答案:起始x坐标+(验证码宽度/验证码字符个数)*第n-1个字符

问题四:如何计算y轴坐标确保验证码上下居中?

答案:(height - (fm.getAscent() + fm.getDescent())) / 2 + fm.getAscent();

理解上述公式之前大家应该先弄清楚什么是基线(baseline)、升度(ascent)、降度(descent)、行间距(lead)

public class CaptchaUtils {
    /**
     * 数字
     */
    private static final String[] DIGIT = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
    /**
     * 字母
     */
    private static final String[] LETTER = {
            "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
            "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
            "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
            "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
    /**
     * 字母数字
     */
    private static final String[] ALPHANUMERIC = {
            "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
            "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
            "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
            "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
            "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
    /**
     * 图片格式
     */
    public static final String FORMAT_NAME = "JPEG";

    /**
     * @param width    画板宽度
     * @param height   画板高度
     * @param count    验证码个数
     * @param fontSize 字体大小
     * @return
     * @Description: 绘制数字图形验证码,不带干扰线
     */
    public static Captcha createDigit(int width, int height, int count, int fontSize) throws IOException {
        return createDigit(width, height, count, fontSize, false, null);
    }

    /**
     * @param width     画板宽度
     * @param height    画板高度
     * @param count     验证码个数
     * @param fontSize  字体大小
     * @param line      是否画干扰线
     * @param lineCount 干扰线数量
     * @return
     * @Description: 绘制数字图形验证码
     */
    public static Captcha createDigit(int width, int height, int count, int fontSize, boolean line, Integer lineCount) throws IOException {
        /**
         * 字体名又分为两大类:中文字体名:宋体,楷体,黑体等;
         *           英文字体名:Arial,Times New Roman等;
         */
        Font font = new Font("Times New Roman", Font.ITALIC, fontSize);
        // 绘制的字符串
        String[] code = getDrawCode(CaptchaType.DIGIT, count);
        return create2D(width, height, code, font, line, lineCount);
    }

    /**
     * @param width    画板宽度
     * @param height   画板高度
     * @param count    验证码个数
     * @param fontSize 字体大小
     * @return
     * @throws IOException
     * @Description: 绘制字母图形验证码
     */
    public static Captcha createLetter(int width, int height, int count, int fontSize) throws IOException {
        return createLetter(width, height, count, fontSize, false, null);
    }

    /**
     * @param width     画板宽度
     * @param height    画板高度
     * @param count     验证码个数
     * @param fontSize  字体大小
     * @param line      是否画干扰线
     * @param lineCount 干扰线数量
     * @return
     * @throws IOException
     * @Description: 绘制字母图形验证码
     */
    public static Captcha createLetter(int width, int height, int count, int fontSize, boolean line, Integer lineCount) throws IOException {
        /**
         * 字体名又分为两大类:中文字体名:宋体,楷体,黑体等;
         *           英文字体名:Arial,Times New Roman等;
         */
        Font font = new Font("Times New Roman", Font.ITALIC, fontSize);
        // 绘制的字符串
        String[] code = getDrawCode(CaptchaType.LETTER, count);
        return create2D(width, height, code, font, line, lineCount);
    }

    /**
     * @param width    画板宽度
     * @param height   画板高度
     * @param count    验证码个数
     * @param fontSize 字体大小
     * @return
     * @throws IOException
     * @Description: 绘制数字字母图形验证码
     */
    public static Captcha createAlphanumeric(int width, int height, int count, int fontSize) throws IOException {
        return createAlphanumeric(width, height, count, fontSize, false, null);
    }

    /**
     * @param width     画板宽度
     * @param height    画板高度
     * @param count     验证码个数
     * @param fontSize  字体大小
     * @param line      是否画干扰线
     * @param lineCount 干扰线数量
     * @return
     * @throws IOException
     * @Description: 绘制数字字母图形验证码
     */
    public static Captcha createAlphanumeric(int width, int height, int count, int fontSize, boolean line, Integer lineCount) throws IOException {
        /**
         * 字体名又分为两大类:中文字体名:宋体,楷体,黑体等;
         *           英文字体名:Arial,Times New Roman等;
         */
        Font font = new Font("Times New Roman", Font.ITALIC, fontSize);
        // 绘制的字符串
        String[] code = getDrawCode(CaptchaType.ALPHANUMERIC, count);
        return create2D(width, height, code, font, line, lineCount);
    }

    /**
     * @param width     画板宽度
     * @param height    画板高度
     * @param code      验证码
     * @param font      字体
     * @param line      是否绘制干扰线
     * @param lineCount 干扰线条数
     * @return 生成的图形验证码对象
     * @throws IOException
     * @Description: 生成图形验证码
     */
    public static Captcha create2D(int width, int height, String[] code, Font font, boolean line, Integer lineCount) throws IOException {
        // 创建空白图片
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 获取图片画笔
        Graphics graphic = image.getGraphics();
        // 绘制矩形背景(实心矩形)
        graphic.fillRect(0, 0, width, height);
        /**
         * Font.PLAIN(普通)
         * Font.BOLD(加粗)
         * Font.ITALIC(斜体)
         * Font.BOLD+ Font.ITALIC(粗斜体)
         */
        graphic.setFont(font);
        // 画图形验证码
        drawCode(graphic, width, height, code);
        // 画干扰线
        if (line) {
            drawLine(graphic, width, height, lineCount);
        }
        ByteArrayOutputStream bas = new ByteArrayOutputStream();
        ImageIO.write(image, FORMAT_NAME, bas);
        return new CaptchaBuilder().code(StringUtils.join(code)).image(bas.toByteArray()).build();

    }

    /**
     * @param graphic 画板对象
     * @param width   画板宽度
     * @param height  画板高度
     * @param code    图形验证码字符串
     * @Description 画图形验证码
     */
    protected static void drawCode(Graphics graphic, int width, int height, String[] code) {
        // 计算文字长度,计算居中的x点坐标,即字符串左边位置
        FontMetrics fm = graphic.getFontMetrics(graphic.getFont());
        // 字符串宽度
        int textWidth = fm.stringWidth(StringUtils.join(code));
        // 字符串左侧x坐标
        int x = (width - textWidth) / 2;
        int y = (height - (fm.getAscent() + fm.getDescent())) / 2 + fm.getAscent();
        for (int i = 0; i < code.length; i++) {
            // 设置随机颜色
            graphic.setColor(new Color(RandomUtils.nextInt(0, 255), RandomUtils.nextInt(0, 255), RandomUtils.nextInt(0, 255)));
            // 绘制字符串
            graphic.drawString(code[i], x + (textWidth / code.length) * i, y);
        }
    }

    /**
     * 获取绘制的验证码
     *
     * @param type  字符串类型
     * @param count 字符个数
     * @return 字符串数组
     */
    protected static String[] getDrawCode(CaptchaType type, int count) {
        String[] code = new String[count];
        Random ran = new Random();
        for (int i = 0; i < count; i++) {
            switch (type) {
                case DIGIT:
                    code[i] = DIGIT[ran.nextInt(DIGIT.length)];
                    break;
                case LETTER:
                    code[i] = LETTER[ran.nextInt(LETTER.length)];
                    break;
                default:
                    code[i] = ALPHANUMERIC[ran.nextInt(ALPHANUMERIC.length)];
                    break;

            }
        }
        return code;
    }

    /**
     * @param graphic   画板对象
     * @param width     画板宽度
     * @param height    画板高度
     * @param lineCount 干扰线条数
     * @Description 绘制干扰线
     */
    protected static void drawLine(Graphics graphic, int width, int height, Integer lineCount) {
        // 画干扰线
        for (int i = 0; i < lineCount; i++) {
            // 设置随机颜色
            graphic.setColor(new Color(RandomUtils.nextInt(0, 255), RandomUtils.nextInt(0, 255), RandomUtils.nextInt(0, 255)));
            // 随机画线
            graphic.drawLine(RandomUtils.nextInt(0, width), RandomUtils.nextInt(0, height), RandomUtils.nextInt(0, width), RandomUtils.nextInt(0, height));
        }
    }
}

案例完整代码请参考:https://github.com/mingyang66/spring-parent

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/488327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LC-2106. 摘水果(滑动窗口)

2106. 摘水果 难度困难78 在一个无限的 x 坐标轴上&#xff0c;有许多水果分布在其中某些位置。给你一个二维整数数组 fruits &#xff0c;其中 fruits[i] [positioni, amounti] 表示共有 amounti 个水果放置在 positioni 上。fruits 已经按 positioni 升序排列 &#xff0c…

C语言动态内存管理与文件操作:打造高效通讯录

本篇博客会讲解如何使用C语言实现一个通讯录。实现通讯录的过程中&#xff0c;会大量用到C语言的知识点&#xff0c;包括但不限于&#xff1a;函数、自定义类型、指针、动态内存管理、文件操作&#xff0c;这些知识点在我的其他博客中都有讲解过&#xff0c;欢迎大家阅读&#…

进程及进程管理

Linux基础 提示&#xff1a;个人学习总结&#xff0c;仅供参考。 一、Linux系统部署 二、服务器初始化 三、文件和用户管理 四、用户的权限 五、进程及进程管理 提示&#xff1a;文档陆续更新整理 进程及进程管理 Linux基础一、进程简介&#xff08;一&#xff09;进程三问…

面向万物智联的应用框架的思考和探索(上)

原文&#xff1a;面向万物智联的应用框架的思考和探索&#xff08;上&#xff09;&#xff0c;点击链接查看更多技术内容。 应用框架&#xff0c;是操作系统连接开发者生态&#xff0c;实现用户体验的关键基础设施。其中&#xff0c;开发效率和运行体验是永恒的诉求&#xff0c…

代码命名规范是真优雅呀!代码如诗

日常编码中&#xff0c;代码的命名是个大的学问。能快速的看懂开源软件的代码结构和意图&#xff0c;也是一项必备的能力。那它们有什么规律呢&#xff1f; Java项目的代码结构&#xff0c;能够体现它的设计理念。Java采用长命名的方式来规范类的命名&#xff0c;能够自己表达…

网络安全怎么学,才不会成为脚本小子?

一&#xff0c;怎么入门&#xff1f; 1、Web 安全相关概念&#xff08;2 周&#xff09; 了解网络安全相关法律法规 熟悉基本概念&#xff08;SQL 注入、上传、XSS、CSRF、一句话木马等&#xff09;。 通过关键字&#xff08;SQL 注入、上传、XSS、CSRF、一句话木马等&#…

RK3588 lt16911uxc hdmi in

我们的单板需要四路HDMI IN,其中一路使用了rk3588自带的hdmi rx,另外三路通过LT6911UXC 转接芯片,实现了三路hdmi in 使用了rk3588的DPHY0,DPHY1以及DCPHY0 设备树修改如下: /* DCPHY0 */ &csi2_dcphy0 { status = "okay"; ports { #addres…

【Errors】【计算机图形学】A-SDF复现的一点纠正记录

ICCV 2021的工作A-SDF&#xff0c;在跑的过程中可能是一些版我Run了这篇工作代码的Reconstruction&#xff0c;然后出现了一点小小的错误&#xff0c;记录如下。 问题一&#xff1a;对数据做直接修改导致出错&#xff08;可能是不同的pytorch版本导致的?&#xff09; 错误描述…

Notepad++ 配置python环境及虚拟环境和其它编程语言环境

背景&#xff1a; 在执行某些项目的时候&#xff0c;经常会否遇到这样一种情况(以python语言为例)&#xff1a;不想在python的基础环境下运行&#xff0c;创建了虚拟环境来装相关项目的包&#xff0c;但是每次使用都要切换到虚拟环境下面使用"activate "命令激活环境…

AT24C02芯片介绍

AT24C02管脚介绍 AT24C02低功耗CMOS串行EEPROM&#xff0c;它是内含2568位存储空间&#xff0c;具有工作电压宽&#xff08;2.5&#xff5e;5.5V&#xff09;、擦写次数多&#xff08;大于10000次&#xff09;、写入速度快&#xff08;小于10ms&#xff09;等特点。 AT24C02的1…

Elasticsearch --- 数据同步、集群

一、数据同步 elasticsearch中的酒店数据来自于mysql数据库&#xff0c;因此mysql数据发生改变时&#xff0c;elasticsearch也必须跟着改变&#xff0c;这个就是elasticsearch与mysql之间的数据同步。 思路分析&#xff1a; 常见的数据同步方案有三种&#xff1a; 同步调用 …

JavaScript:链表

文章目录 链表js没有链表吗203. 移除链表元素为什么设置虚拟头节点思路与代码分析上面代码补充分析注意&#xff1a;为什么把虚拟头节点赋值给 cur 204. 设计链表206. 反转链表双指针法--具体思路见代码双指针法具体分析&#xff08;上面代码看懂这里可以忽略&#xff09;递归法…

注册openai用什么邮箱-中文版ChatGPT有哪些

注册openai用什么邮箱 你可以使用任何有效的电子邮箱地址注册 OpenAI&#xff0c;例如 Gmail、Outlook、Yahoo 等。请注意&#xff0c;您需要通过验证您的电子邮件地址才能完成注册过程。 中文版ChatGPT哪个好 中文版的 ChatGPT 在自然语言处理、语言生成等领域拥有广泛的应用…

4.4.1内核编译

内核源码下载地址&#xff1a; https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.4.1.tar.gz 安装依赖包&#xff1a;报错就装 cp /boot/config-xxx ./.config make mrproper make menuconfig,然后save保存&#xff0c;退出 make -j4 //四线程编译 sudo ma…

电气电工相关专业知识及名词解释

一、电流电压 火线、零线、地线&#xff1a;火线和零线的区别就是&#xff1a;火线带电&#xff0c;零线不带电。火线是传电流的&#xff0c;而零线是回流的。 红色是火线&#xff0c;零线一般是绿色的&#xff0c;通常可用电笔来测。电笔一头亮了是火线&#xff0c;不亮的则…

网络计算模式复习(一)

二层C/S架构 概念&#xff1a;C/S架构是一种典型的两层架构&#xff0c;其全称是Client/Server&#xff0c;即客户端服务器端架构。 其中客户端包含一个或多个在用户的电脑上运行的程序。服务器端有两种&#xff0c;一种是数据库服务器端&#xff0c;客户通过数据库连接访问服…

GBASE荣获2023数字中国创新大赛·信创赛道华北赛区一等奖

日前&#xff0c;2023数字中国创新大赛信创赛道华北赛区区域赛圆满结束。经过激烈角逐&#xff0c;GBASE参赛作品「多模多态企业级分布式数据库」GBase 8c荣获华北赛区一等奖&#xff0c;将被选送到全国总决赛&#xff0c;参与最后的冠军角逐。 数字中国创新大赛信创赛道是我国…

RIS/PACS系统源码,工作站无缝集成三维重建模块,实现极速的三维后处理

RIS/PACS系统源码 带三维重建和还原的PACS源码 RIS/PACS系统源码在预约登记、分诊叫号、技师检查、诊断报告、临床浏览、科室管理等环节满足全院相关科室的要求。在医学影像下载、浏览、处理中满足速度快、强化常用功能、方便阅片等要求。满足放射、超声、内镜、病理等影像科…

REMIX:重构·连接·进化|徐亚波博士D3大会演讲实录

“欢迎大家和数说故事一起来到新世界&#xff0c;和我们一起&#xff0c;来玩一个AI普适场景的无限游戏。” 在数说故事第六届D3智能营销峰会上&#xff0c;数说故事创始人兼CEO徐亚波博士带来「REMIX——重构连接进化」的主题分享&#xff0c;聚焦“ChatGPT开启的AGI时代有什么…

分块思想(Sqrt Decomposition)的实现(golang)

前言 Sqrt Decomposition 是一种数据结构&#xff0c;能够在O(1)时间复杂度内完成数组元素值的查询和更新&#xff0c;在 O ( n ) O(\sqrt{n}) O(n ​) 时间复杂度内完成数组某个区间属性值的查询和批量更新某个区间的值。这里的属性 可以是区间的和、最小值、最大值等。 说到…