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

com.github.zh9131101.utils.RendererUtils Maven / Gradle / Ivy

/*
 * Copyright 2021-2039 ZH9131101.
 *
 * Licensed 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 com.github.zh9131101.utils;

import com.github.zh9131101.constant.CaptchaConst;
import com.github.zh9131101.textimage.AbstractCaptcha;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 

* 验证码渲染 工具类 *

* * @author zh9131101 * @version V1.0.0 * @date 2021-01-07 22:08 * @since 1.0 */ public class RendererUtils { private RendererUtils() { } /** * 渲染验证码 * * @param formatType 图片格式(后缀) * @param os 输出流 * @param captcha 验证码 * @return true or false */ public static boolean renderer(String formatType, OutputStream os, AbstractCaptcha captcha) { if (StringUtils.isEmpty(formatType)) { formatType = CaptchaConst.PNG; } boolean success; if (CaptchaConst.GIF.equals(formatType)) { success = rendererGif(os, captcha); } else { success = rendererImage(formatType, os, captcha); } return success; } /** * 渲染图片验证码 * * @param formatType 图片格式(后缀) * @param os 输出流 * @param captcha 验证码 * @return true or false */ private static boolean rendererImage(String formatType, OutputStream os, AbstractCaptcha captcha) { try { checkCaptcha(captcha); // 验证码图片宽度 Integer width = captcha.getWidth(); // 验证码图片长度 int height = captcha.getHeight(); checkFont(captcha); Font font = captcha.getFont(); char[] captchaChars = captcha.getCaptchaChars().toCharArray(); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = (Graphics2D) bi.getGraphics(); // 填充背景 g2d.setColor(captcha.getBgColor() == null ? Color.WHITE : captcha.getBgColor()); g2d.fillRect(0, 0, width, height); // 抗锯齿 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 画干扰圆 drawObstructOval(width, height, captcha.getObstructOvalCount(), null, g2d); // 画干扰线 drawObstructLine(width, height, captcha.getObstructLineCount(), null, g2d); // 画干扰点 drawObstructPoint(width, height, captcha.getObstructPointCount(), null, g2d); // 画干扰线(贝塞尔曲线) g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); drawBezierCurves(width, height, captcha.getBezierCurvesCount(), null, g2d); // 画字符串 g2d.setFont(font); FontMetrics fontMetrics = g2d.getFontMetrics(); // 每一个字符所占的宽度 int fW = width / captchaChars.length; // 字符的左右边距 int fSp = (fW - (int) fontMetrics.getStringBounds(alphaPattern(String.valueOf(captchaChars)), g2d).getWidth()) / 2; for (int i = 0; i < captchaChars.length; i++) { g2d.setColor(color()); // 文字的纵坐标 int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(captchaChars[i]), g2d).getHeight()) >> 1); g2d.drawString(String.valueOf(captchaChars[i]), i * fW + fSp + 3, fY - 3); } g2d.dispose(); ImageIO.write(bi, formatType, os); os.flush(); return true; } catch (IOException e) { e.printStackTrace(); } finally { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } return false; } /** * 渲染GIF图片验证码 * * @param os 输出流 * @param captcha 验证码 * @return true or false */ private static boolean rendererGif(OutputStream os, AbstractCaptcha captcha) { try { checkCaptcha(captcha); // 验证码图片宽度 Integer width = captcha.getWidth(); // 验证码图片长度 int height = captcha.getHeight(); // 随机生成每个文字的颜色 int len = captcha.getCaptchaChars().length(); Color[] fontColor = new Color[len]; for (int i = 0; i < len; i++) { fontColor[i] = color(); } // 随机生成贝塞尔曲线参数 int x1 = 5; int y1 = RandomUtils.randomNumber(5, height / 2); int x2 = width - 5; int y2 = RandomUtils.randomNumber(height / 2, height - 5); int ctrlx = RandomUtils.randomNumber(width / 4, width / 4 * 3); int ctrly = RandomUtils.randomNumber(5, height - 5); if (RandomUtils.randomNumber(2) == 0) { int ty = y1; y1 = y2; y2 = ty; } int ctrlx1 = RandomUtils.randomNumber(width / 4, width / 4 * 3); int ctrly1 = RandomUtils.randomNumber(5, height - 5); int[][] besselXY = new int[][]{{x1, y1}, {ctrlx, ctrly}, {ctrlx1, ctrly1}, {x2, y2}}; // 开始画gif每一帧 AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder(); gifEncoder.setQuality(180); gifEncoder.setDelay(100); gifEncoder.setRepeat(0); gifEncoder.start(os); for (int i = 0; i < captcha.getCaptchaChars().length(); i++) { BufferedImage frame = graphicsGifImage(captcha, fontColor, i, besselXY); gifEncoder.addFrame(frame); frame.flush(); } gifEncoder.finish(); return true; } catch (Exception e) { e.printStackTrace(); } finally { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } return false; } /** * 画gif随机验证码图 * * @param captcha 验证码 * @param fontColor 随机字体颜色 * @param flag 透明度 * @param besselXY 干扰线参数 * @return BufferedImage */ private static BufferedImage graphicsGifImage(AbstractCaptcha captcha, Color[] fontColor, int flag, int[][] besselXY) { Integer width = captcha.getWidth(); Integer height = captcha.getHeight(); checkFont(captcha); Font font = captcha.getFont(); char[] captchaChars = captcha.getCaptchaChars().toCharArray(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = (Graphics2D) image.getGraphics(); // 填充背景颜色 g2d.setColor(captcha.getBgColor() == null ? Color.WHITE : captcha.getBgColor()); g2d.fillRect(0, 0, width, height); // 抗锯齿 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 画干扰圆圈, 设置透明度 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f * RandomUtils.randomNumber(10))); drawObstructOval(width, height, captcha.getObstructOvalCount(), null, g2d); // 画干扰线 drawObstructLine(width, height, captcha.getObstructLineCount(), null, g2d); // 画干扰点 drawObstructPoint(width, height, captcha.getObstructPointCount(), null, g2d); // 画干扰线, 设置透明度 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); drawBezierCurves(width, height, captcha.getBezierCurvesCount(), null, g2d); g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); g2d.setColor(fontColor[0]); CubicCurve2D shape = new CubicCurve2D.Double(besselXY[0][0], besselXY[0][1], besselXY[1][0], besselXY[1][1], besselXY[2][0], besselXY[2][1], besselXY[3][0], besselXY[3][1]); g2d.draw(shape); // 画验证码 g2d.setFont(font); FontMetrics fontMetrics = g2d.getFontMetrics(); int len = captchaChars.length; // 每一个字符所占的宽度 int fW = width / len; // 字符的左右边距 int fSp = (fW - (int) fontMetrics.getStringBounds(alphaPattern(String.valueOf(captchaChars)), g2d).getWidth()) / 2; for (int i = 0; i < len; i++) { // 设置透明度 AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha(len, flag, i)); g2d.setComposite(ac3); g2d.setColor(fontColor[i % fontColor.length]); // 文字的纵坐标 int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(captchaChars[i]), g2d).getHeight()) >> 1); g2d.drawString(String.valueOf(captchaChars[i]), i * fW + fSp + 3, fY - 3); } g2d.dispose(); return image; } /** * 检查生成验证码 * * @param captcha 验证码抽象类 */ private static void checkCaptcha(AbstractCaptcha captcha) { if (StringUtils.isEmpty(captcha.getCaptcha()) || StringUtils.isEmpty(captcha.getCaptchaChars())) { captcha.generateCaptcha(); } } /** * 检查生成字体 * * @param captcha 验证码抽象类 */ private static void checkFont(AbstractCaptcha captcha) { Font font = captcha.getFont(); if (font == null) { font = RendererUtils.createFont(captcha.getFontPath(), captcha.getFontStyle(), captcha.getFontSize(), captcha.getFontName()); captcha.setFont(font); } } /** * 设置字体左右边距 * * @param captcha 验证码 * @return String */ private static String alphaPattern(String captcha) { String patternString = captcha.trim(); // 正则匹配数字 String number = "\\d+"; // 正则匹配中文 String chinese = "[\\u4e00-\\u9fa5]+"; Pattern compile = Pattern.compile(number); Matcher matcher = compile.matcher(patternString); if (matcher.matches()) { return "8"; } compile = Pattern.compile(chinese); matcher = compile.matcher(patternString); if (matcher.matches()) { return "苏"; } return "Z"; } /** * 获取透明度,从0到1,自动计算步长 * * @param length 长度 * @param i 验证码字符数组遍历值 * @param j 验证码字符数组遍历值 * @return 透明度 */ private static float alpha(Integer length, Integer i, Integer j) { int num = i + j; float r = (float) 1 / (length - 1); float s = length * r; return num >= length ? (num * r - s) : num * r; } /** * 随机画干扰线 * * @param num 数量 * @param color 颜色 * @param g Graphics2D */ public static void drawObstructLine(Integer width, Integer height, int num, Color color, Graphics2D g) { for (int i = 0; i < num; i++) { g.setColor(color == null ? color() : color); int x1 = RandomUtils.randomNumber(-10, width - 10); int y1 = RandomUtils.randomNumber(5, height - 5); int x2 = RandomUtils.randomNumber(10, width + 10); int y2 = RandomUtils.randomNumber(2, height - 2); g.drawLine(x1, y1, x2, y2); } } /** * 随机产生干扰点 * * @param num 数量 * @param color 颜色 * @param g Graphics2D */ private static void drawObstructPoint(Integer width, Integer height, int num, Color color, Graphics2D g) { // 随机产生干扰点 for (int i = 0; i < num; i++) { int x = RandomUtils.randomNumber(width); int y = RandomUtils.randomNumber(height); g.setColor(color == null ? color() : color); g.drawOval(x, y, RandomUtils.randomNumber(3), RandomUtils.randomNumber(3)); } } /** * 随机画干扰圆 * * @param num 数量 * @param color 颜色 * @param g Graphics2D */ private static void drawObstructOval(Integer width, Integer height, int num, Color color, Graphics2D g) { for (int i = 0; i < num; i++) { g.setColor(color == null ? color() : color); int w = 5 + RandomUtils.randomNumber(10); g.drawOval(RandomUtils.randomNumber(width - 25), RandomUtils.randomNumber(height - 15), w, w); } } /** * 随机画贝塞尔曲线 * * @param num 数量 * @param color 颜色 * @param g Graphics2D */ private static void drawBezierCurves(Integer width, Integer height, int num, Color color, Graphics2D g) { for (int i = 0; i < num; i++) { g.setColor(color == null ? color() : color); int x1 = 5; int y1 = RandomUtils.randomNumber(5, height / 2); int x2 = width - 5; int y2 = RandomUtils.randomNumber(height / 2, height - 5); int ctrlx = RandomUtils.randomNumber(width / 4, width / 4 * 3); int ctrly = RandomUtils.randomNumber(5, height - 5); if (RandomUtils.randomNumber(2) == 0) { int ty = y1; y1 = y2; y2 = ty; } // 二阶贝塞尔曲线 if (RandomUtils.randomNumber(2) == 0) { QuadCurve2D shape = new QuadCurve2D.Double(); shape.setCurve(x1, y1, ctrlx, ctrly, x2, y2); g.draw(shape); } else { // 三阶贝塞尔曲线 int ctrlx1 = RandomUtils.randomNumber(width / 4, width / 4 * 3); int ctrly1 = RandomUtils.randomNumber(5, height - 5); CubicCurve2D shape = new CubicCurve2D.Double(x1, y1, ctrlx, ctrly, ctrlx1, ctrly1, x2, y2); g.draw(shape); } } } /** * 指定颜色中获取随机颜色值 * * @return RGB颜色 */ public static Color color() { int[] rgb = CaptchaConst.COLOR[RandomUtils.randomNumber(CaptchaConst.COLOR.length)]; return new Color(rgb[0], rgb[1], rgb[2]); } /** * 创建字体 * * @return 字体 */ public static Font createFont(String fontPath, Integer fontStyle, Integer fontSize, String fontName) { Font font; try { String path = fontPath; if (StringUtils.isEmpty(fontPath)) { path = StringUtils.FOLDER_SEPARATOR + "font" + StringUtils.FOLDER_SEPARATOR; } else { if (!fontPath.startsWith(StringUtils.FOLDER_SEPARATOR)) { path = StringUtils.FOLDER_SEPARATOR + fontPath; } if (!fontPath.endsWith(StringUtils.FOLDER_SEPARATOR)) { path = fontPath + StringUtils.FOLDER_SEPARATOR; } } if (fontSize == null || fontSize <= 0) { fontSize = 28; } if (fontStyle == null || fontStyle <= 0) { fontStyle = Font.PLAIN; } if (StringUtils.isEmpty(fontName)) { fontName = "microsoft_yahei.ttf"; } font = Font.createFont(Font.TRUETYPE_FONT, RendererUtils.class.getResourceAsStream(path + fontName)).deriveFont(fontStyle, fontSize); } catch (Exception e) { font = new Font("Arial", Font.PLAIN, 28); e.printStackTrace(); } return font; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy