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) {
return this;
}
init = true;
try {
log.info("图片验证码[{}]初始化...", this.getClass().getSimpleName());
doInit();
} catch (Exception e) {
init = false;
log.error("[{}]初始化失败,ex", this.getClass().getSimpleName(), e);
throw e;
}
init = true;
return this;
}
@@ -322,7 +322,7 @@ public class CaptchaImageUtils {
public static BufferedImage drawWordImg(Color fontColor,
String word,
Font font,
FontDesignMetrics metrics,
float fontTopCoef,
int imgWidth,
int imgHeight,
float deg) {
@@ -333,7 +333,7 @@ public class CaptchaImageUtils {
g.setColor(fontColor);
g.setFont(font);
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.drawString(word, left, top);
g.dispose();
@@ -405,7 +405,6 @@ public class CaptchaImageUtils {
*
* @param data 验证码内容
* @param font 字体包
* @param metrics FontDesignMetrics
* @param width 验证码宽度
* @param height 验证码高度
* @param startX 起始X
@@ -416,7 +415,6 @@ public class CaptchaImageUtils {
*/
public static BufferedImage genSimpleImgCaptcha(String data,
Font font,
FontDesignMetrics metrics,
int width,
int height,
float startX,
@@ -1,6 +1,5 @@
package cloud.tianai.captcha.generator.impl;
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.common.model.dto.GenerateParam;
@@ -69,49 +68,46 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
private void init(int z) {
this.size = z;
// 初始化一个队列扫描
scheduledExecutor.scheduleAtFixedRate(() -> {
queueMap.forEach((k, queue) -> {
try {
AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0));
int addCount = 0;
while (pos.get() < this.size) {
if (pos.get() >= size) {
return;
}
ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(k);
if (slideImageInfo != null) {
boolean addStatus = queue.offer(slideImageInfo);
addCount++;
if (addStatus) {
// 添加记录
pos.incrementAndGet();
}
} else {
sleep();
}
scheduledExecutor.scheduleAtFixedRate(() -> queueMap.forEach((k, queue) -> {
try {
AtomicInteger pos = posMap.computeIfAbsent(k, k1 -> new AtomicInteger(0));
int addCount = 0;
while (pos.get() < this.size) {
if (pos.get() >= size) {
return;
}
if (addCount == 0) {
// 没有添加,检测最新更新时间 如果时间过长,直接清除数据
Long lastUpdate = lastUpdateMap.get(k);
if (lastUpdate != null && System.currentTimeMillis() - lastUpdate > expireTime) {
queueMap.remove(k);
posMap.remove(k);
lastUpdateMap.remove(k);
ImageCaptchaInfo slideImageInfo = target.generateCaptchaImage(k);
if (slideImageInfo != null) {
boolean addStatus = queue.offer(slideImageInfo);
addCount++;
if (addStatus) {
// 添加记录
pos.incrementAndGet();
}
} else {
sleep();
}
} catch (Exception e) {
// cache所有
log.error("缓存队列扫描时出错, ex", e);
// 删掉它
queueMap.remove(k);
posMap.remove(k);
lastUpdateMap.remove(k);
// 休眠
sleep();
}
});
}, 0, period, TimeUnit.MILLISECONDS);
if (addCount == 0) {
// 没有添加,检测最新更新时间 如果时间过长,直接清除数据
Long lastUpdate = lastUpdateMap.get(k);
if (lastUpdate != null && System.currentTimeMillis() - lastUpdate > expireTime) {
queueMap.remove(k);
posMap.remove(k);
lastUpdateMap.remove(k);
}
}
} catch (Exception e) {
// cache所有
log.error("缓存队列扫描时出错, ex", e);
// 删掉它
queueMap.remove(k);
posMap.remove(k);
lastUpdateMap.remove(k);
// 休眠
sleep();
}
}), 0, period, TimeUnit.MILLISECONDS);
}
private void sleep() {
@@ -121,6 +117,11 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
}
}
@Override
public ImageCaptchaGenerator init() {
return target.init();
}
@SneakyThrows
@Override
public ImageCaptchaInfo generateCaptchaImage(String type) {
@@ -176,20 +177,4 @@ public class CacheImageCaptchaGenerator implements ImageCaptchaGenerator {
public ImageCaptchaResourceManager 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.Setter;
import lombok.SneakyThrows;
import sun.font.FontDesignMetrics;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@@ -36,9 +37,6 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
protected Font font;
@Getter
@Setter
protected FontDesignMetrics metrics;
@Getter
@Setter
protected Integer clickImgWidth = 80;
@Getter
@Setter
@@ -50,6 +48,16 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
@Setter
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
public StandardRandomWordClickImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) {
super(imageCaptchaResourceManager, initDefaultResource);
@@ -67,7 +75,8 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream);
this.font = font.deriveFont(Font.BOLD, 70);
}
this.metrics = FontDesignMetrics.getMetrics(font);
// 计算容错
currentFontTopCoef = coef * font.getSize() + intercept;
if (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")));
}
@SneakyThrows
@Override
public ImgWrapper genTipImage(List<ClickImageCheckDefinition> imageCheckDefinitions) {
String tips = imageCheckDefinitions.stream().map(ClickImageCheckDefinition::getTip).collect(Collectors.joining());
// 生成随机颜色
int fontWidth = metrics.stringWidth(tips);
int width = fontWidth + 5;
int height = metrics.getHeight() + 5;
int fontWidth = tips.length() * font.getSize();
int width = fontWidth + 6;
int height = font.getSize() + 6;
float left = (width - fontWidth) / 2f;
float top = 5 / 2f + metrics.getAscent();
float top = 6 / 2f + font.getSize() - currentFontTopCoef;
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);
}
@SneakyThrows
@Override
public ImgWrapper randomGetClickImg() {
ThreadLocalRandom random = ThreadLocalRandom.current();
@@ -114,10 +128,13 @@ public class StandardRandomWordClickImageCaptchaGenerator extends AbstractClickI
BufferedImage fontImage = CaptchaImageUtils.drawWordImg(randomColor,
randomWord,
font,
this.metrics,
currentFontTopCoef,
clickImgWidth,
clickImgHeight,
randomDeg);
FileOutputStream fileOutputStream = new FileOutputStream("d:/aaa/" + randomWord + ".png");
ImageIO.write(fontImage, "png", fileOutputStream);
fileOutputStream.close();
return new ImgWrapper(fontImage, randomWord);
}