U 解决 java17默认不支持 FontDesignMetrics 类的问题, 故剔除该类 使用其它算法替代

This commit is contained in:
天爱有情
2022-05-18 15:56:38 +08:00
parent f16234e457
commit f4e7c9c2e8
5 changed files with 77 additions and 74 deletions
@@ -57,13 +57,15 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
if (init) { if (init) {
return this; return this;
} }
init = true;
try { try {
log.info("图片验证码[{}]初始化...", this.getClass().getSimpleName());
doInit(); doInit();
} catch (Exception e) { } catch (Exception e) {
init = false;
log.error("[{}]初始化失败,ex", this.getClass().getSimpleName(), e); log.error("[{}]初始化失败,ex", this.getClass().getSimpleName(), e);
throw e; throw e;
} }
init = true;
return this; return this;
} }
@@ -322,7 +322,7 @@ public class CaptchaImageUtils {
public static BufferedImage drawWordImg(Color fontColor, public static BufferedImage drawWordImg(Color fontColor,
String word, String word,
Font font, Font font,
FontDesignMetrics metrics, float fontTopCoef,
int imgWidth, int imgWidth,
int imgHeight, int imgHeight,
float deg) { float deg) {
@@ -333,7 +333,7 @@ public class CaptchaImageUtils {
g.setColor(fontColor); g.setColor(fontColor);
g.setFont(font); g.setFont(font);
float left = (imgWidth - font.getSize()) / 2f; float left = (imgWidth - font.getSize()) / 2f;
float top = (imgHeight - font.getSize()) / 2f + metrics.getAscent() - 6; float top = (imgHeight - font.getSize()) / 2f + font.getSize() - fontTopCoef;
g.rotate(Math.toRadians(deg), imgWidth / 2f, imgHeight / 2f); g.rotate(Math.toRadians(deg), imgWidth / 2f, imgHeight / 2f);
g.drawString(word, left, top); g.drawString(word, left, top);
g.dispose(); g.dispose();
@@ -405,7 +405,6 @@ public class CaptchaImageUtils {
* *
* @param data 验证码内容 * @param data 验证码内容
* @param font 字体包 * @param font 字体包
* @param metrics FontDesignMetrics
* @param width 验证码宽度 * @param width 验证码宽度
* @param height 验证码高度 * @param height 验证码高度
* @param startX 起始X * @param startX 起始X
@@ -416,7 +415,6 @@ public class CaptchaImageUtils {
*/ */
public static BufferedImage genSimpleImgCaptcha(String data, public static BufferedImage genSimpleImgCaptcha(String data,
Font font, Font font,
FontDesignMetrics metrics,
int width, int width,
int height, int height,
float startX, float startX,
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.generator.impl; package cloud.tianai.captcha.generator.impl;
import cloud.tianai.captcha.common.util.NamedThreadFactory;
import cloud.tianai.captcha.common.util.NamedThreadFactory; import cloud.tianai.captcha.common.util.NamedThreadFactory;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator; import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
@@ -69,8 +68,7 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
private void init(int z) { private void init(int z) {
this.size = z; this.size = z;
// 初始化一个队列扫描 // 初始化一个队列扫描
scheduledExecutor.scheduleAtFixedRate(() -> { scheduledExecutor.scheduleAtFixedRate(() -> queueMap.forEach((k, queue) -> {
queueMap.forEach((k, queue) -> {
try { try {
AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0)); AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0));
int addCount = 0; int addCount = 0;
@@ -109,9 +107,7 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
// 休眠 // 休眠
sleep(); sleep();
} }
}); }), 0, period, TimeUnit.MILLISECONDS);
}, 0, period, TimeUnit.MILLISECONDS);
} }
private void sleep() { private void sleep() {
@@ -121,6 +117,11 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
} }
} }
@Override
public ImageCaptchaGenerator init() {
return target.init();
}
@SneakyThrows @SneakyThrows
@Override @Override
public ImageCaptchaInfo generateCaptchaImage(String type) { public ImageCaptchaInfo generateCaptchaImage(String type) {
@@ -176,20 +177,4 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
public ImageCaptchaResourceManager getImageResourceManager() { public ImageCaptchaResourceManager getImageResourceManager() {
return target.getImageResourceManager(); return target.getImageResourceManager();
} }
// public static void main(String[] args) throws InterruptedException {
// SliderCaptchaTemplate captchaTemplate = new DefaultSliderCaptchaTemplate("jpeg", "png", true);
//
// captchaTemplate = new CacheSliderCaptchaTemplate(captchaTemplate, 20);
// TimeUnit.SECONDS.sleep(5);
// for (int i = 0; i < 100; i++) {
// long start = System.currentTimeMillis();
// SliderCaptchaInfo info = captchaTemplate.getSlideImageInfo();
// long end = System.currentTimeMillis();
// System.out.println("耗时:" + (end - start));
// TimeUnit.MILLISECONDS.sleep(10);
// }
// }
} }
@@ -13,10 +13,11 @@ import cloud.tianai.captcha.resource.impl.provider.ClassPathResourceProvider;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import sun.font.FontDesignMetrics;
import javax.imageio.ImageIO;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
@@ -36,9 +37,6 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
protected Font font; protected Font font;
@Getter @Getter
@Setter @Setter
protected FontDesignMetrics metrics;
@Getter
@Setter
protected Integer clickImgWidth = 80; protected Integer clickImgWidth = 80;
@Getter @Getter
@Setter @Setter
@@ -50,6 +48,16 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
@Setter @Setter
protected int tipImageInterferencePointNum = 5; protected int tipImageInterferencePointNum = 5;
/**
* 因为在画文字图形的时候 y 值不能准确通过 除法计算得出, 字体大小不一致中间的容错值不可估计,
* 所以通过 线性回归模型 计算出 intercept和coef 用于计算 容错值
* 训练数据为 宋体 字体大小为 30~150 随机选择7组数据进行训练, 训练后r2结果为 0.9967106324620846
*/
protected float intercept = 0.39583333f;
protected float coef = 0.14645833f;
protected float currentFontTopCoef = 0.0f;
@SneakyThrows @SneakyThrows
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) { public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
super(imageCaptchaResourceManager, initDefaultResource); super(imageCaptchaResourceManager, initDefaultResource);
@@ -67,7 +75,8 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream); Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
this.font = font.deriveFont(Font.BOLD, 70); this.font = font.deriveFont(Font.BOLD, 70);
} }
this.metrics = FontDesignMetrics.getMetrics(font); // 计算容错
currentFontTopCoef = coef * font.getSize() + intercept;
if (initDefaultResource) { if (initDefaultResource) {
initDefaultResource(); initDefaultResource();
} }
@@ -88,20 +97,25 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); resourceStore.addResource(CaptchaTypeConstant.WORD_IMAGE_CLICK, new Resource(ClassPathResourceProvider.NAME, StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg")));
} }
@SneakyThrows
@Override @Override
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) { public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining()); String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining());
// 生成随机颜色 // 生成随机颜色
int fontWidth = metrics.stringWidth(tips); int fontWidth = tips.length() * font.getSize();
int width = fontWidth + 5; int width = fontWidth + 6;
int height = metrics.getHeight() + 5; int height = font.getSize() + 6;
float left = (width - fontWidth) / 2f; float left = (width - fontWidth) / 2f;
float top = 5 / 2f + metrics.getAscent(); float top = 6 / 2f + font.getSize() - currentFontTopCoef;
BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips, BufferedImage bufferedImage = CaptchaImageUtils.genSimpleImgCaptcha(tips,
font, metrics, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum); font, width, height, left, top, tipImageInterferenceLineNum, tipImageInterferencePointNum);
FileOutputStream fileOutputStream = new FileOutputStream("d:/aaa/" + tips + ".png");
ImageIO.write(bufferedImage, "png", fileOutputStream);
fileOutputStream.close();
return new ImgWrapper(bufferedImage, tips); return new ImgWrapper(bufferedImage, tips);
} }
@SneakyThrows
@Override @Override
public ImgWrapper randomGetClickImg() { public ImgWrapper randomGetClickImg() {
ThreadLocalRandom random = ThreadLocalRandom.current(); ThreadLocalRandom random = ThreadLocalRandom.current();
@@ -114,10 +128,13 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor, BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
randomWord, randomWord,
font, font,
this.metrics, currentFontTopCoef,
clickImgWidth, clickImgWidth,
clickImgHeight, clickImgHeight,
randomDeg); randomDeg);
FileOutputStream fileOutputStream = new FileOutputStream("d:/aaa/" + randomWord + ".png");
ImageIO.write(fontImage, "png", fileOutputStream);
fileOutputStream.close();
return new ImgWrapper(fontImage, randomWord); return new ImgWrapper(fontImage, randomWord);
} }
@@ -1,6 +1,7 @@
package example; package example;
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant; import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam; import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.impl.StandardRandomWordClickImageCaptchaGenerator; import cloud.tianai.captcha.generator.impl.StandardRandomWordClickImageCaptchaGenerator;
@@ -15,8 +16,8 @@ public class StandardWordClickImageCaptchaGeneratorTest {
public static void main(String[] args) { public static void main(String[] args) {
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager(); ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
StandardRandomWordClickImageCaptchaGenerator defaultImageCaptchaResourceManager = ImageCaptchaGenerator defaultImageCaptchaResourceManager =
new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, true); new StandardRandomWordClickImageCaptchaGenerator(imageCaptchaResourceManager, true).init();
GenerateParam generateParam = new GenerateParam(); GenerateParam generateParam = new GenerateParam();
generateParam.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK); generateParam.setType(CaptchaTypeConstant.WORD_IMAGE_CLICK);