();
67 | prepare(expression);
68 | Collections.reverse(postfixStack);// 将后缀式栈反转
69 | String firstValue, secondValue, currentValue;// 参与计算的第一个值,第二个值和算术运算符
70 | while (!postfixStack.isEmpty()) {
71 | currentValue = postfixStack.pop();
72 | if (!isOperator(currentValue.charAt(0))) {// 如果不是运算符则存入操作数栈中
73 | currentValue = currentValue.replace("~", "-");
74 | resultStack.push(currentValue);
75 | } else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
76 | secondValue = resultStack.pop();
77 | firstValue = resultStack.pop();
78 |
79 | // 将负数标记符改为负号
80 | firstValue = firstValue.replace("~", "-");
81 | secondValue = secondValue.replace("~", "-");
82 |
83 | String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
84 | resultStack.push(tempResult);
85 | }
86 | }
87 | return Double.valueOf(resultStack.pop());
88 | }
89 |
90 | /**
91 | * 数据准备阶段将表达式转换成为后缀式栈
92 | *
93 | * @param expression
94 | */
95 | private void prepare(String expression) {
96 | opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低
97 | char[] arr = expression.toCharArray();
98 | int currentIndex = 0;// 当前字符的位置
99 | int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
100 | char currentOp, peekOp;// 当前操作符和栈顶操作符
101 | for (int i = 0; i < arr.length; i++) {
102 | currentOp = arr[i];
103 | if (isOperator(currentOp)) {// 如果当前字符是运算符
104 | if (count > 0) {
105 | postfixStack.push(new String(arr, currentIndex, count));// 取两个运算符之间的数字
106 | }
107 | peekOp = opStack.peek();
108 | if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
109 | while (opStack.peek() != '(') {
110 | postfixStack.push(String.valueOf(opStack.pop()));
111 | }
112 | opStack.pop();
113 | } else {
114 | while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
115 | postfixStack.push(String.valueOf(opStack.pop()));
116 | peekOp = opStack.peek();
117 | }
118 | opStack.push(currentOp);
119 | }
120 | count = 0;
121 | currentIndex = i + 1;
122 | } else {
123 | count++;
124 | }
125 | }
126 | if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
127 | postfixStack.push(new String(arr, currentIndex, count));
128 | }
129 |
130 | while (opStack.peek() != ',') {
131 | postfixStack.push(String.valueOf(opStack.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
132 | }
133 | }
134 |
135 | /**
136 | * 判断是否为算术符号
137 | *
138 | * @param c
139 | * @return
140 | */
141 | private boolean isOperator(char c) {
142 | return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
143 | }
144 |
145 | /**
146 | * 利用ASCII码-40做下标去算术符号优先级
147 | *
148 | * @param cur
149 | * @param peek
150 | * @return
151 | */
152 | public boolean compare(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
153 | boolean result = false;
154 | if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
155 | result = true;
156 | }
157 | return result;
158 | }
159 |
160 | /**
161 | * 按照给定的算术运算符做计算
162 | *
163 | * @param firstValue
164 | * @param secondValue
165 | * @param currentOp
166 | * @return
167 | */
168 | private String calculate(String firstValue, String secondValue, char currentOp) {
169 | String result = "";
170 | switch (currentOp) {
171 | case '+':
172 | result = String.valueOf(ArithHelper.add(firstValue, secondValue));
173 | break;
174 | case '-':
175 | result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
176 | break;
177 | case '*':
178 | result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
179 | break;
180 | case '/':
181 | result = String.valueOf(ArithHelper.div(firstValue, secondValue));
182 | break;
183 | }
184 | return result;
185 | }
186 | }
187 |
188 |
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/base/Captcha.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.base;
2 |
3 | import java.awt.*;
4 | import java.awt.geom.CubicCurve2D;
5 | import java.awt.geom.QuadCurve2D;
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 | import java.io.OutputStream;
9 | import java.util.Base64;
10 |
11 | /**
12 | * 验证码抽象类
13 | * Created by 王帆 on 2018-07-27 上午 10:08.
14 | */
15 | public abstract class Captcha extends Randoms {
16 | // 常用颜色
17 | public static final int[][] COLOR = {{0, 135, 255}, {51, 153, 51}, {255, 102, 102}, {255, 153, 0}, {153, 102, 0}, {153, 102, 153}, {51, 153, 153}, {102, 102, 255}, {0, 102, 204}, {204, 51, 51}, {0, 153, 204}, {0, 51, 102}};
18 | // 验证码文本类型
19 | public static final int TYPE_DEFAULT = 1; // 字母数字混合
20 | public static final int TYPE_ONLY_NUMBER = 2; // 纯数字
21 | public static final int TYPE_ONLY_CHAR = 3; // 纯字母
22 | public static final int TYPE_ONLY_UPPER = 4; // 纯大写字母
23 | public static final int TYPE_ONLY_LOWER = 5; // 纯小写字母
24 | public static final int TYPE_NUM_AND_UPPER = 6; // 数字大写字母
25 | // 内置字体
26 | public static final int FONT_1 = 0;
27 | public static final int FONT_2 = 1;
28 | public static final int FONT_3 = 2;
29 | public static final int FONT_4 = 3;
30 | public static final int FONT_5 = 4;
31 | public static final int FONT_6 = 5;
32 | public static final int FONT_7 = 6;
33 | public static final int FONT_8 = 7;
34 | public static final int FONT_9 = 8;
35 | public static final int FONT_10 = 9;
36 | private static final String[] FONT_NAMES = new String[]{"actionj.ttf", "epilog.ttf", "fresnel.ttf", "headache.ttf", "lexo.ttf", "prefix.ttf", "progbot.ttf", "ransom.ttf", "robot.ttf", "scandal.ttf"};
37 | private Font font = null; // 验证码的字体
38 | protected int len = 5; // 验证码随机字符长度
39 | protected int width = 130; // 验证码显示宽度
40 | protected int height = 48; // 验证码显示高度
41 | protected int charType = TYPE_DEFAULT; // 验证码类型
42 | protected String chars = null; // 当前验证码
43 |
44 | /**
45 | * 生成随机验证码
46 | *
47 | * @return 验证码字符数组
48 | */
49 | protected char[] alphas() {
50 | char[] cs = new char[len];
51 | for (int i = 0; i < len; i++) {
52 | switch (charType) {
53 | case 2:
54 | cs[i] = alpha(numMaxIndex);
55 | break;
56 | case 3:
57 | cs[i] = alpha(charMinIndex, charMaxIndex);
58 | break;
59 | case 4:
60 | cs[i] = alpha(upperMinIndex, upperMaxIndex);
61 | break;
62 | case 5:
63 | cs[i] = alpha(lowerMinIndex, lowerMaxIndex);
64 | break;
65 | case 6:
66 | cs[i] = alpha(upperMaxIndex);
67 | break;
68 | default:
69 | cs[i] = alpha();
70 | }
71 | }
72 | chars = new String(cs);
73 | return cs;
74 | }
75 |
76 | /**
77 | * 给定范围获得随机颜色
78 | *
79 | * @param fc 0-255
80 | * @param bc 0-255
81 | * @return 随机颜色
82 | */
83 | protected Color color(int fc, int bc) {
84 | if (fc > 255)
85 | fc = 255;
86 | if (bc > 255)
87 | bc = 255;
88 | int r = fc + num(bc - fc);
89 | int g = fc + num(bc - fc);
90 | int b = fc + num(bc - fc);
91 | return new Color(r, g, b);
92 | }
93 |
94 | /**
95 | * 获取随机常用颜色
96 | *
97 | * @return 随机颜色
98 | */
99 | protected Color color() {
100 | int[] color = COLOR[num(COLOR.length)];
101 | return new Color(color[0], color[1], color[2]);
102 | }
103 |
104 | /**
105 | * 验证码输出,抽象方法,由子类实现
106 | *
107 | * @param os 输出流
108 | * @return 是否成功
109 | */
110 | public abstract boolean out(OutputStream os);
111 |
112 | /**
113 | * 输出base64编码
114 | *
115 | * @return base64编码字符串
116 | */
117 | public abstract String toBase64();
118 |
119 | /**
120 | * 输出base64编码
121 | *
122 | * @param type 编码头
123 | * @return base64编码字符串
124 | */
125 | public String toBase64(String type) {
126 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
127 | out(outputStream);
128 | return type + Base64.getEncoder().encodeToString(outputStream.toByteArray());
129 | }
130 |
131 | /**
132 | * 获取当前的验证码
133 | *
134 | * @return 字符串
135 | */
136 | public String text() {
137 | checkAlpha();
138 | return chars;
139 | }
140 |
141 | /**
142 | * 获取当前验证码的字符数组
143 | *
144 | * @return 字符数组
145 | */
146 | public char[] textChar() {
147 | checkAlpha();
148 | return chars.toCharArray();
149 | }
150 |
151 | /**
152 | * 检查验证码是否生成,没有则立即生成
153 | */
154 | public void checkAlpha() {
155 | if (chars == null) {
156 | alphas(); // 生成验证码
157 | }
158 | }
159 |
160 | /**
161 | * 随机画干扰线
162 | *
163 | * @param num 数量
164 | * @param g Graphics2D
165 | */
166 | public void drawLine(int num, Graphics2D g) {
167 | drawLine(num, null, g);
168 | }
169 |
170 | /**
171 | * 随机画干扰线
172 | *
173 | * @param num 数量
174 | * @param color 颜色
175 | * @param g Graphics2D
176 | */
177 | public void drawLine(int num, Color color, Graphics2D g) {
178 | for (int i = 0; i < num; i++) {
179 | g.setColor(color == null ? color() : color);
180 | int x1 = num(-10, width - 10);
181 | int y1 = num(5, height - 5);
182 | int x2 = num(10, width + 10);
183 | int y2 = num(2, height - 2);
184 | g.drawLine(x1, y1, x2, y2);
185 | }
186 | }
187 |
188 | /**
189 | * 随机画干扰圆
190 | *
191 | * @param num 数量
192 | * @param g Graphics2D
193 | */
194 | public void drawOval(int num, Graphics2D g) {
195 | drawOval(num, null, g);
196 | }
197 |
198 | /**
199 | * 随机画干扰圆
200 | *
201 | * @param num 数量
202 | * @param color 颜色
203 | * @param g Graphics2D
204 | */
205 | public void drawOval(int num, Color color, Graphics2D g) {
206 | for (int i = 0; i < num; i++) {
207 | g.setColor(color == null ? color() : color);
208 | int w = 5 + num(10);
209 | g.drawOval(num(width - 25), num(height - 15), w, w);
210 | }
211 | }
212 |
213 | /**
214 | * 随机画贝塞尔曲线
215 | *
216 | * @param num 数量
217 | * @param g Graphics2D
218 | */
219 | public void drawBesselLine(int num, Graphics2D g) {
220 | drawBesselLine(num, null, g);
221 | }
222 |
223 | /**
224 | * 随机画贝塞尔曲线
225 | *
226 | * @param num 数量
227 | * @param color 颜色
228 | * @param g Graphics2D
229 | */
230 | public void drawBesselLine(int num, Color color, Graphics2D g) {
231 | for (int i = 0; i < num; i++) {
232 | g.setColor(color == null ? color() : color);
233 | int x1 = 5, y1 = num(5, height / 2);
234 | int x2 = width - 5, y2 = num(height / 2, height - 5);
235 | int ctrlx = num(width / 4, width / 4 * 3), ctrly = num(5, height - 5);
236 | if (num(2) == 0) {
237 | int ty = y1;
238 | y1 = y2;
239 | y2 = ty;
240 | }
241 | if (num(2) == 0) { // 二阶贝塞尔曲线
242 | QuadCurve2D shape = new QuadCurve2D.Double();
243 | shape.setCurve(x1, y1, ctrlx, ctrly, x2, y2);
244 | g.draw(shape);
245 | } else { // 三阶贝塞尔曲线
246 | int ctrlx1 = num(width / 4, width / 4 * 3), ctrly1 = num(5, height - 5);
247 | CubicCurve2D shape = new CubicCurve2D.Double(x1, y1, ctrlx, ctrly, ctrlx1, ctrly1, x2, y2);
248 | g.draw(shape);
249 | }
250 | }
251 | }
252 |
253 | public Font getFont() {
254 | if (font == null) {
255 | try {
256 | setFont(FONT_1);
257 | } catch (Exception e) {
258 | setFont(new Font("Arial", Font.BOLD, 32));
259 | }
260 | }
261 | return font;
262 | }
263 |
264 | public void setFont(Font font) {
265 | this.font = font;
266 | }
267 |
268 | public void setFont(int font) throws IOException, FontFormatException {
269 | setFont(font, 32f);
270 | }
271 |
272 | public void setFont(int font, float size) throws IOException, FontFormatException {
273 | setFont(font, Font.BOLD, size);
274 | }
275 |
276 | public void setFont(int font, int style, float size) throws IOException, FontFormatException {
277 | this.font = Font.createFont(Font.TRUETYPE_FONT, getClass().getResourceAsStream("/" + FONT_NAMES[font])).deriveFont(style, size);
278 | }
279 |
280 | public int getLen() {
281 | return len;
282 | }
283 |
284 | public void setLen(int len) {
285 | this.len = len;
286 | }
287 |
288 | public int getWidth() {
289 | return width;
290 | }
291 |
292 | public void setWidth(int width) {
293 | this.width = width;
294 | }
295 |
296 | public int getHeight() {
297 | return height;
298 | }
299 |
300 | public void setHeight(int height) {
301 | this.height = height;
302 | }
303 |
304 | public int getCharType() {
305 | return charType;
306 | }
307 |
308 | public void setCharType(int charType) {
309 | this.charType = charType;
310 | }
311 | }
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/base/ChineseCaptchaAbstract.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.base;
2 |
3 | import java.awt.*;
4 |
5 | /**
6 | * 中文验证码抽象类
7 | * Created by 王帆 on 2018-07-27 上午 10:08.
8 | */
9 | public abstract class ChineseCaptchaAbstract extends Captcha {
10 | // 常用汉字
11 | public static final String DELTA = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
12 |
13 | public ChineseCaptchaAbstract() {
14 | setFont(new Font("楷体", Font.PLAIN, 28));
15 | setLen(4);
16 | }
17 |
18 | /**
19 | * 生成随机验证码
20 | *
21 | * @return 验证码字符数组
22 | */
23 | @Override
24 | protected char[] alphas() {
25 | char[] cs = new char[len];
26 | for (int i = 0; i < len; i++) {
27 | cs[i] = alphaHan();
28 | }
29 | chars = new String(cs);
30 | return cs;
31 | }
32 |
33 | /**
34 | * 返回随机汉字
35 | *
36 | * @return 随机汉字
37 | */
38 | public static char alphaHan() {
39 | return DELTA.charAt(num(DELTA.length()));
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/base/Randoms.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.base;
2 |
3 | import java.security.SecureRandom;
4 |
5 | /**
6 | * 随机数工具类
7 | * Created by 王帆 on 2018-07-27 上午 10:08.
8 | */
9 | public class Randoms {
10 | protected static final SecureRandom RANDOM = new SecureRandom();
11 | // 定义验证码字符.去除了0、O、I、L等容易混淆的字母
12 | public static final char ALPHA[] = {'2', '3', '4', '5', '6', '7', '8', '9',
13 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
14 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
15 | protected static final int numMaxIndex = 8; // 数字的最大索引,不包括最大值
16 | protected static final int charMinIndex = numMaxIndex; // 字符的最小索引,包括最小值
17 | protected static final int charMaxIndex = ALPHA.length; // 字符的最大索引,不包括最大值
18 | protected static final int upperMinIndex = charMinIndex; // 大写字符最小索引
19 | protected static final int upperMaxIndex = upperMinIndex + 23; // 大写字符最大索引
20 | protected static final int lowerMinIndex = upperMaxIndex; // 小写字母最小索引
21 | protected static final int lowerMaxIndex = charMaxIndex; // 小写字母最大索引
22 |
23 | /**
24 | * 产生两个数之间的随机数
25 | *
26 | * @param min 最小值
27 | * @param max 最大值
28 | * @return 随机数
29 | */
30 | public static int num(int min, int max) {
31 | return min + RANDOM.nextInt(max - min);
32 | }
33 |
34 | /**
35 | * 产生0-num的随机数,不包括num
36 | *
37 | * @param num 最大值
38 | * @return 随机数
39 | */
40 | public static int num(int num) {
41 | return RANDOM.nextInt(num);
42 | }
43 |
44 | /**
45 | * 返回ALPHA中的随机字符
46 | *
47 | * @return 随机字符
48 | */
49 | public static char alpha() {
50 | return ALPHA[num(ALPHA.length)];
51 | }
52 |
53 | /**
54 | * 返回ALPHA中第0位到第num位的随机字符
55 | *
56 | * @param num 到第几位结束
57 | * @return 随机字符
58 | */
59 | public static char alpha(int num) {
60 | return ALPHA[num(num)];
61 | }
62 |
63 | /**
64 | * 返回ALPHA中第min位到第max位的随机字符
65 | *
66 | * @param min 从第几位开始
67 | * @param max 到第几位结束
68 | * @return 随机字符
69 | */
70 | public static char alpha(int min, int max) {
71 | return ALPHA[num(min, max)];
72 | }
73 | }
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/servlet/CaptchaServlet.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.servlet;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.ServletException;
6 | import javax.servlet.http.HttpServlet;
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletResponse;
9 |
10 | import com.wf.captcha.utils.CaptchaUtil;
11 |
12 | /**
13 | * 验证码servlet
14 | * Created by 王帆 on 2018-07-27 上午 10:08.
15 | */
16 | public class CaptchaServlet extends HttpServlet {
17 | private static final long serialVersionUID = -90304944339413093L;
18 |
19 | public void doGet(HttpServletRequest request, HttpServletResponse response)
20 | throws ServletException, IOException {
21 | CaptchaUtil.out(request, response);
22 | }
23 |
24 | public void doPost(HttpServletRequest request, HttpServletResponse response)
25 | throws ServletException, IOException {
26 | doGet(request, response);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/utils/CaptchaUtil.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.utils;
2 |
3 | import java.awt.*;
4 | import java.io.IOException;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 |
9 | import com.wf.captcha.base.Captcha;
10 | import com.wf.captcha.SpecCaptcha;
11 |
12 | /**
13 | * 图形验证码工具类
14 | * Created by 王帆 on 2018-07-27 上午 10:08.
15 | */
16 | public class CaptchaUtil {
17 | private static final String SESSION_KEY = "captcha";
18 | private static final int DEFAULT_LEN = 4; // 默认长度
19 | private static final int DEFAULT_WIDTH = 130; // 默认宽度
20 | private static final int DEFAULT_HEIGHT = 48; // 默认高度
21 |
22 | /**
23 | * 输出验证码
24 | *
25 | * @param request HttpServletRequest
26 | * @param response HttpServletResponse
27 | * @throws IOException IO异常
28 | */
29 | public static void out(HttpServletRequest request, HttpServletResponse response)
30 | throws IOException {
31 | out(DEFAULT_LEN, request, response);
32 | }
33 |
34 | /**
35 | * 输出验证码
36 | *
37 | * @param len 长度
38 | * @param request HttpServletRequest
39 | * @param response HttpServletResponse
40 | * @throws IOException IO异常
41 | */
42 | public static void out(int len, HttpServletRequest request, HttpServletResponse response)
43 | throws IOException {
44 | out(DEFAULT_WIDTH, DEFAULT_HEIGHT, len, request, response);
45 | }
46 |
47 | /**
48 | * 输出验证码
49 | *
50 | * @param width 宽度
51 | * @param height 高度
52 | * @param len 长度
53 | * @param request HttpServletRequest
54 | * @param response HttpServletResponse
55 | * @throws IOException IO异常
56 | */
57 | public static void out(int width, int height, int len, HttpServletRequest request, HttpServletResponse response)
58 | throws IOException {
59 | out(width, height, len, null, request, response);
60 | }
61 |
62 | /**
63 | * 输出验证码
64 | *
65 | * @param font 字体
66 | * @param request HttpServletRequest
67 | * @param response HttpServletResponse
68 | * @throws IOException IO异常
69 | */
70 | public static void out(Font font, HttpServletRequest request, HttpServletResponse response)
71 | throws IOException {
72 | out(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_LEN, font, request, response);
73 | }
74 |
75 | /**
76 | * 输出验证码
77 | *
78 | * @param len 长度
79 | * @param font 字体
80 | * @param request HttpServletRequest
81 | * @param response HttpServletResponse
82 | * @throws IOException IO异常
83 | */
84 | public static void out(int len, Font font, HttpServletRequest request, HttpServletResponse response)
85 | throws IOException {
86 | out(DEFAULT_WIDTH, DEFAULT_HEIGHT, len, font, request, response);
87 | }
88 |
89 | /**
90 | * 输出验证码
91 | *
92 | * @param width 宽度
93 | * @param height 高度
94 | * @param len 长度
95 | * @param font 字体
96 | * @param request HttpServletRequest
97 | * @param response HttpServletResponse
98 | * @throws IOException IO异常
99 | */
100 | public static void out(int width, int height, int len, Font font, HttpServletRequest request, HttpServletResponse response)
101 | throws IOException {
102 | SpecCaptcha specCaptcha = new SpecCaptcha(width, height, len);
103 | if (font != null) {
104 | specCaptcha.setFont(font);
105 | }
106 | out(specCaptcha, request, response);
107 | }
108 |
109 |
110 | /**
111 | * 输出验证码
112 | *
113 | * @param captcha Captcha
114 | * @param request HttpServletRequest
115 | * @param response HttpServletResponse
116 | * @throws IOException IO异常
117 | */
118 | public static void out(Captcha captcha, HttpServletRequest request, HttpServletResponse response)
119 | throws IOException {
120 | setHeader(response);
121 | request.getSession().setAttribute(SESSION_KEY, captcha.text().toLowerCase());
122 | captcha.out(response.getOutputStream());
123 | }
124 |
125 | /**
126 | * 验证验证码
127 | *
128 | * @param code 用户输入的验证码
129 | * @param request HttpServletRequest
130 | * @return 是否正确
131 | */
132 | public static boolean ver(String code, HttpServletRequest request) {
133 | if (code != null) {
134 | String captcha = (String) request.getSession().getAttribute(SESSION_KEY);
135 | return code.trim().toLowerCase().equals(captcha);
136 | }
137 | return false;
138 | }
139 |
140 | /**
141 | * 清除session中的验证码
142 | *
143 | * @param request HttpServletRequest
144 | */
145 | public static void clear(HttpServletRequest request) {
146 | request.getSession().removeAttribute(SESSION_KEY);
147 | }
148 |
149 | /**
150 | * 设置相应头
151 | *
152 | * @param response HttpServletResponse
153 | */
154 | public static void setHeader(HttpServletResponse response) {
155 | response.setContentType("image/gif");
156 | response.setHeader("Pragma", "No-cache");
157 | response.setHeader("Cache-Control", "no-cache");
158 | response.setDateHeader("Expires", 0);
159 | }
160 |
161 | }
162 |
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/utils/Encoder.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.utils;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 |
6 | /**
7 | *
8 | */
9 | public class Encoder {
10 | private static final int EOF = -1;
11 | // 图片的宽高
12 | private int imgW, imgH;
13 | private byte[] pixAry;
14 | private int initCodeSize; // 验证码位数
15 | private int remaining; // 剩余数量
16 | private int curPixel; // 像素
17 |
18 | static final int BITS = 12;
19 |
20 | static final int HSIZE = 5003; // 80% 占用率
21 |
22 | int n_bits; // number of bits/code
23 | int maxbits = BITS; // user settable max # bits/code
24 | int maxcode; // maximum code, given n_bits
25 | int maxmaxcode = 1 << BITS; // should NEVER generate this code
26 |
27 | int[] htab = new int[HSIZE];
28 | int[] codetab = new int[HSIZE];
29 |
30 | int hsize = HSIZE; // for dynamic table sizing
31 |
32 | int free_ent = 0; // first unused entry
33 |
34 | // block compression parameters -- after all codes are used up,
35 | // and compression rate changes, start over.
36 | boolean clear_flg = false;
37 |
38 | // Algorithm: use open addressing double hashing (no chaining) on the
39 | // prefix code / next character combination. We do a variant of Knuth's
40 | // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
41 | // secondary probe. Here, the modular division first probe is gives way
42 | // to a faster exclusive-or manipulation. Also do block compression with
43 | // an adaptive reset, whereby the code table is cleared when the compression
44 | // ratio decreases, but after the table fills. The variable-length output
45 | // codes are re-sized at this point, and a special CLEAR code is generated
46 | // for the decompressor. Late addition: construct the table according to
47 | // file size for noticeable speed improvement on small files. Please direct
48 | // questions about this implementation to ames!jaw.
49 |
50 | int g_init_bits;
51 |
52 | int ClearCode;
53 | int EOFCode;
54 |
55 | // output
56 | //
57 | // Output the given code.
58 | // Inputs:
59 | // code: A n_bits-bit integer. If == -1, then EOF. This assumes
60 | // that n_bits =< wordsize - 1.
61 | // Outputs:
62 | // Outputs code to the file.
63 | // Assumptions:
64 | // Chars are 8 bits long.
65 | // Algorithm:
66 | // Maintain a BITS character long buffer (so that 8 codes will
67 | // fit in it exactly). Use the VAX insv instruction to insert each
68 | // code in turn. When the buffer fills up empty it and start over.
69 |
70 | int cur_accum = 0;
71 | int cur_bits = 0;
72 |
73 | int masks[] =
74 | {
75 | 0x0000,
76 | 0x0001,
77 | 0x0003,
78 | 0x0007,
79 | 0x000F,
80 | 0x001F,
81 | 0x003F,
82 | 0x007F,
83 | 0x00FF,
84 | 0x01FF,
85 | 0x03FF,
86 | 0x07FF,
87 | 0x0FFF,
88 | 0x1FFF,
89 | 0x3FFF,
90 | 0x7FFF,
91 | 0xFFFF};
92 |
93 | // Number of characters so far in this 'packet'
94 | int a_count;
95 |
96 | // Define the storage for the packet accumulator
97 | byte[] accum = new byte[256];
98 |
99 | //----------------------------------------------------------------------------
100 |
101 | /**
102 | * @param width 宽度
103 | * @param height 高度
104 | * @param pixels 像素
105 | * @param color_depth 颜色
106 | */
107 | Encoder(int width, int height, byte[] pixels, int color_depth) {
108 | imgW = width;
109 | imgH = height;
110 | pixAry = pixels;
111 | initCodeSize = Math.max(2, color_depth);
112 | }
113 |
114 | // Add a character to the end of the current packet, and if it is 254
115 | // characters, flush the packet to disk.
116 |
117 | /**
118 | * @param c 字节
119 | * @param outs 输出流
120 | * @throws IOException IO异常
121 | */
122 | void char_out(byte c, OutputStream outs) throws IOException {
123 | accum[a_count++] = c;
124 | if (a_count >= 254)
125 | flush_char(outs);
126 | }
127 |
128 | // Clear out the hash table
129 |
130 | // table clear for block compress
131 |
132 | /**
133 | * @param outs 输出流
134 | * @throws IOException IO异常
135 | */
136 | void cl_block(OutputStream outs) throws IOException {
137 | cl_hash(hsize);
138 | free_ent = ClearCode + 2;
139 | clear_flg = true;
140 |
141 | output(ClearCode, outs);
142 | }
143 |
144 | // reset code table
145 |
146 | /**
147 | * @param hsize int
148 | */
149 | void cl_hash(int hsize) {
150 | for (int i = 0; i < hsize; ++i)
151 | htab[i] = -1;
152 | }
153 |
154 | /**
155 | * @param init_bits int
156 | * @param outs 输出流
157 | * @throws IOException IO异常
158 | */
159 | void compress(int init_bits, OutputStream outs) throws IOException {
160 | int fcode;
161 | int i /* = 0 */;
162 | int c;
163 | int ent;
164 | int disp;
165 | int hsize_reg;
166 | int hshift;
167 |
168 | // Set up the globals: g_init_bits - initial number of bits
169 | g_init_bits = init_bits;
170 |
171 | // Set up the necessary values
172 | clear_flg = false;
173 | n_bits = g_init_bits;
174 | maxcode = MAXCODE(n_bits);
175 |
176 | ClearCode = 1 << (init_bits - 1);
177 | EOFCode = ClearCode + 1;
178 | free_ent = ClearCode + 2;
179 |
180 | a_count = 0; // clear packet
181 |
182 | ent = nextPixel();
183 |
184 | hshift = 0;
185 | for (fcode = hsize; fcode < 65536; fcode *= 2)
186 | ++hshift;
187 | hshift = 8 - hshift; // set hash code range bound
188 |
189 | hsize_reg = hsize;
190 | cl_hash(hsize_reg); // clear hash table
191 |
192 | output(ClearCode, outs);
193 |
194 | outer_loop:
195 | while ((c = nextPixel()) != EOF) {
196 | fcode = (c << maxbits) + ent;
197 | i = (c << hshift) ^ ent; // xor hashing
198 |
199 | if (htab[i] == fcode) {
200 | ent = codetab[i];
201 | continue;
202 | } else if (htab[i] >= 0) // non-empty slot
203 | {
204 | disp = hsize_reg - i; // secondary hash (after G. Knott)
205 | if (i == 0)
206 | disp = 1;
207 | do {
208 | if ((i -= disp) < 0)
209 | i += hsize_reg;
210 |
211 | if (htab[i] == fcode) {
212 | ent = codetab[i];
213 | continue outer_loop;
214 | }
215 | } while (htab[i] >= 0);
216 | }
217 | output(ent, outs);
218 | ent = c;
219 | if (free_ent < maxmaxcode) {
220 | codetab[i] = free_ent++; // code -> hashtable
221 | htab[i] = fcode;
222 | } else
223 | cl_block(outs);
224 | }
225 | // Put out the final code.
226 | output(ent, outs);
227 | output(EOFCode, outs);
228 | }
229 |
230 | //----------------------------------------------------------------------------
231 |
232 | /**
233 | * @param os 输出流
234 | * @throws IOException IO异常
235 | */
236 | void encode(OutputStream os) throws IOException {
237 | os.write(initCodeSize); // write "initial code size" byte
238 |
239 | remaining = imgW * imgH; // reset navigation variables
240 | curPixel = 0;
241 |
242 | compress(initCodeSize + 1, os); // compress and write the pixel data
243 |
244 | os.write(0); // write block terminator
245 | }
246 |
247 | // Flush the packet to disk, and reset the accumulator
248 |
249 | /**
250 | * @param outs 输出流
251 | * @throws IOException IO异常
252 | */
253 | void flush_char(OutputStream outs) throws IOException {
254 | if (a_count > 0) {
255 | outs.write(a_count);
256 | outs.write(accum, 0, a_count);
257 | a_count = 0;
258 | }
259 | }
260 |
261 | /**
262 | * @param n_bits int
263 | * @return int
264 | */
265 | final int MAXCODE(int n_bits) {
266 | return (1 << n_bits) - 1;
267 | }
268 |
269 | //----------------------------------------------------------------------------
270 | // Return the next pixel from the image
271 | //----------------------------------------------------------------------------
272 |
273 | /**
274 | * @return int
275 | */
276 | private int nextPixel() {
277 | if (remaining == 0)
278 | return EOF;
279 |
280 | --remaining;
281 |
282 | byte pix = pixAry[curPixel++];
283 |
284 | return pix & 0xff;
285 | }
286 |
287 | /**
288 | * @param code int
289 | * @param outs 输出流
290 | * @throws IOException IO异常
291 | */
292 | void output(int code, OutputStream outs) throws IOException {
293 | cur_accum &= masks[cur_bits];
294 |
295 | if (cur_bits > 0)
296 | cur_accum |= (code << cur_bits);
297 | else
298 | cur_accum = code;
299 |
300 | cur_bits += n_bits;
301 |
302 | while (cur_bits >= 8) {
303 | char_out((byte) (cur_accum & 0xff), outs);
304 | cur_accum >>= 8;
305 | cur_bits -= 8;
306 | }
307 |
308 | // If the next entry is going to be too big for the code size,
309 | // then increase it, if possible.
310 | if (free_ent > maxcode || clear_flg) {
311 | if (clear_flg) {
312 | maxcode = MAXCODE(n_bits = g_init_bits);
313 | clear_flg = false;
314 | } else {
315 | ++n_bits;
316 | if (n_bits == maxbits)
317 | maxcode = maxmaxcode;
318 | else
319 | maxcode = MAXCODE(n_bits);
320 | }
321 | }
322 |
323 | if (code == EOFCode) {
324 | // At EOF, write the rest of the buffer.
325 | while (cur_bits > 0) {
326 | char_out((byte) (cur_accum & 0xff), outs);
327 | cur_accum >>= 8;
328 | cur_bits -= 8;
329 | }
330 |
331 | flush_char(outs);
332 | }
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/utils/GifEncoder.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.utils;
2 |
3 | import java.awt.*;
4 | import java.awt.image.BufferedImage;
5 | import java.awt.image.DataBufferByte;
6 | import java.io.BufferedOutputStream;
7 | import java.io.ByteArrayOutputStream;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.io.OutputStream;
11 |
12 | /**
13 | * Gif生成工具
14 | * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or
15 | * more frames.
16 | *
17 | * Example:
18 | * AnimatedGifEncoder e = new AnimatedGifEncoder();
19 | * e.start(outputFileName);
20 | * e.setDelay(1000); // 1 frame per sec
21 | * e.addFrame(image1);
22 | * e.addFrame(image2);
23 | * e.finish();
24 | *
25 | * No copyright asserted on the source code of this class. May be used
26 | * for any purpose, however, refer to the Unisys LZW patent for restrictions
27 | * on use of the associated Encoder class. Please forward any corrections
28 | * to questions at fmsware.com.
29 | */
30 | public class GifEncoder {
31 | protected int width; // image size
32 | protected int height;
33 | protected Color transparent = null; // transparent color if given
34 | protected int transIndex; // transparent index in color table
35 | protected int repeat = -1; // no repeat
36 | protected int delay = 0; // frame delay (hundredths)
37 | protected boolean started = false; // ready to output frames
38 | protected OutputStream out;
39 | protected BufferedImage image; // current frame
40 | protected byte[] pixels; // BGR byte array from frame
41 | protected byte[] indexedPixels; // converted frame indexed to palette
42 | protected int colorDepth; // number of bit planes
43 | protected byte[] colorTab; // RGB palette
44 | protected boolean[] usedEntry = new boolean[256]; // active palette entries
45 | protected int palSize = 7; // color table size (bits-1)
46 | protected int dispose = -1; // disposal code (-1 = use default)
47 | protected boolean closeStream = false; // close stream when finished
48 | protected boolean firstFrame = true;
49 | protected boolean sizeSet = false; // if false, get size from first frame
50 | protected int sample = 10; // default sample interval for quantizer
51 |
52 | /**
53 | * Sets the delay time between each frame, or changes it
54 | * for subsequent frames (applies to last frame added).
55 | *
56 | * @param ms int delay time in milliseconds
57 | */
58 | public void setDelay(int ms) {
59 | delay = Math.round(ms / 10.0f);
60 | }
61 |
62 | /**
63 | * Sets the GIF frame disposal code for the last added frame
64 | * and any subsequent frames. Default is 0 if no transparent
65 | * color has been set, otherwise 2.
66 | *
67 | * @param code int disposal code.
68 | */
69 | public void setDispose(int code) {
70 | if (code >= 0) {
71 | dispose = code;
72 | }
73 | }
74 |
75 | /**
76 | * Sets the number of times the set of GIF frames
77 | * should be played. Default is 1; 0 means play
78 | * indefinitely. Must be invoked before the first
79 | * image is added.
80 | *
81 | * @param iter int number of iterations.
82 | */
83 | public void setRepeat(int iter) {
84 | if (iter >= 0) {
85 | repeat = iter;
86 | }
87 | }
88 |
89 | /**
90 | * Sets the transparent color for the last added frame
91 | * and any subsequent frames.
92 | * Since all colors are subject to modification
93 | * in the quantization process, the color in the final
94 | * palette for each frame closest to the given color
95 | * becomes the transparent color for that frame.
96 | * May be set to null to indicate no transparent color.
97 | *
98 | * @param c Color to be treated as transparent on display.
99 | */
100 | public void setTransparent(Color c) {
101 | transparent = c;
102 | }
103 |
104 | /**
105 | * Adds next GIF frame. The frame is not written immediately, but is
106 | * actually deferred until the next frame is received so that timing
107 | * data can be inserted. Invoking finish()
flushes all
108 | * frames. If setSize
was not invoked, the size of the
109 | * first image is used for all subsequent frames.
110 | *
111 | * @param im BufferedImage containing frame to write.
112 | * @return true if successful.
113 | */
114 | public boolean addFrame(BufferedImage im) {
115 | if ((im == null) || !started) {
116 | return false;
117 | }
118 | boolean ok = true;
119 | try {
120 | if (!sizeSet) {
121 | // use first frame's size
122 | setSize(im.getWidth(), im.getHeight());
123 | }
124 | image = im;
125 | getImagePixels(); // convert to correct format if necessary
126 | analyzePixels(); // build color table & map pixels
127 | if (firstFrame) {
128 | writeLSD(); // logical screen descriptior
129 | writePalette(); // global color table
130 | if (repeat >= 0) {
131 | // use NS app extension to indicate reps
132 | writeNetscapeExt();
133 | }
134 | }
135 | writeGraphicCtrlExt(); // write graphic control extension
136 | writeImageDesc(); // image descriptor
137 | if (!firstFrame) {
138 | writePalette(); // local color table
139 | }
140 | writePixels(); // encode and write pixel data
141 | firstFrame = false;
142 | } catch (IOException e) {
143 | ok = false;
144 | }
145 |
146 | return ok;
147 | }
148 |
149 | //added by alvaro
150 | public boolean outFlush() {
151 | boolean ok = true;
152 | try {
153 | out.flush();
154 | return ok;
155 | } catch (IOException e) {
156 | ok = false;
157 | }
158 |
159 | return ok;
160 | }
161 |
162 | public byte[] getFrameByteArray() {
163 | return ((ByteArrayOutputStream) out).toByteArray();
164 | }
165 |
166 | /**
167 | * Flushes any pending data and closes output file.
168 | * If writing to an OutputStream, the stream is not
169 | * closed.
170 | *
171 | * @return boolean
172 | */
173 | public boolean finish() {
174 | if (!started) return false;
175 | boolean ok = true;
176 | started = false;
177 | try {
178 | out.write(0x3b); // gif trailer
179 | out.flush();
180 | if (closeStream) {
181 | out.close();
182 | }
183 | } catch (IOException e) {
184 | ok = false;
185 | }
186 |
187 | return ok;
188 | }
189 |
190 | public void reset() {
191 | // reset for subsequent use
192 | transIndex = 0;
193 | out = null;
194 | image = null;
195 | pixels = null;
196 | indexedPixels = null;
197 | colorTab = null;
198 | closeStream = false;
199 | firstFrame = true;
200 | }
201 |
202 | /**
203 | * Sets frame rate in frames per second. Equivalent to
204 | * setDelay(1000/fps)
.
205 | *
206 | * @param fps float frame rate (frames per second)
207 | */
208 | public void setFrameRate(float fps) {
209 | if (fps != 0f) {
210 | delay = Math.round(100f / fps);
211 | }
212 | }
213 |
214 | /**
215 | * Sets quality of color quantization (conversion of images
216 | * to the maximum 256 colors allowed by the GIF specification).
217 | * Lower values (minimum = 1) produce better colors, but slow
218 | * processing significantly. 10 is the default, and produces
219 | * good color mapping at reasonable speeds. Values greater
220 | * than 20 do not yield significant improvements in speed.
221 | *
222 | * @param quality int greater than 0.
223 | */
224 | public void setQuality(int quality) {
225 | if (quality < 1) quality = 1;
226 | sample = quality;
227 | }
228 |
229 | /**
230 | * Sets the GIF frame size. The default size is the
231 | * size of the first frame added if this method is
232 | * not invoked.
233 | *
234 | * @param w int frame width.
235 | * @param h int frame width.
236 | */
237 | public void setSize(int w, int h) {
238 | if (started && !firstFrame) return;
239 | width = w;
240 | height = h;
241 | if (width < 1) width = 320;
242 | if (height < 1) height = 240;
243 | sizeSet = true;
244 | }
245 |
246 | /**
247 | * Initiates GIF file creation on the given stream. The stream
248 | * is not closed automatically.
249 | *
250 | * @param os OutputStream on which GIF images are written.
251 | * @return false if initial write failed.
252 | */
253 | public boolean start(OutputStream os) {
254 | if (os == null) return false;
255 | boolean ok = true;
256 | closeStream = false;
257 | out = os;
258 | try {
259 | writeString("GIF89a"); // header
260 | } catch (IOException e) {
261 | ok = false;
262 | }
263 | return started = ok;
264 | }
265 |
266 | /**
267 | * Initiates writing of a GIF file with the specified name.
268 | *
269 | * @param file String containing output file name.
270 | * @return false if open or initial write failed.
271 | */
272 | public boolean start(String file) {
273 | boolean ok = true;
274 | try {
275 | out = new BufferedOutputStream(new FileOutputStream(file));
276 | ok = start(out);
277 | closeStream = true;
278 | } catch (IOException e) {
279 | ok = false;
280 | }
281 | return started = ok;
282 | }
283 |
284 | /**
285 | * Analyzes image colors and creates color map.
286 | */
287 | protected void analyzePixels() {
288 | int len = pixels.length;
289 | int nPix = len / 3;
290 | indexedPixels = new byte[nPix];
291 | Quant nq = new Quant(pixels, len, sample);
292 | // initialize quantizer
293 | colorTab = nq.process(); // create reduced palette
294 | // convert map from BGR to RGB
295 | for (int i = 0; i < colorTab.length; i += 3) {
296 | byte temp = colorTab[i];
297 | colorTab[i] = colorTab[i + 2];
298 | colorTab[i + 2] = temp;
299 | usedEntry[i / 3] = false;
300 | }
301 | // map image pixels to new palette
302 | int k = 0;
303 | for (int i = 0; i < nPix; i++) {
304 | int index =
305 | nq.map(pixels[k++] & 0xff,
306 | pixels[k++] & 0xff,
307 | pixels[k++] & 0xff);
308 | usedEntry[index] = true;
309 | indexedPixels[i] = (byte) index;
310 | }
311 | pixels = null;
312 | colorDepth = 8;
313 | palSize = 7;
314 | // get closest match to transparent color if specified
315 | if (transparent != null) {
316 | transIndex = findClosest(transparent);
317 | }
318 | }
319 |
320 | /**
321 | * Returns index of palette color closest to c
322 | *
323 | * @param c color
324 | * @return int
325 | */
326 | protected int findClosest(Color c) {
327 | if (colorTab == null) return -1;
328 | int r = c.getRed();
329 | int g = c.getGreen();
330 | int b = c.getBlue();
331 | int minpos = 0;
332 | int dmin = 256 * 256 * 256;
333 | int len = colorTab.length;
334 | for (int i = 0; i < len; ) {
335 | int dr = r - (colorTab[i++] & 0xff);
336 | int dg = g - (colorTab[i++] & 0xff);
337 | int db = b - (colorTab[i] & 0xff);
338 | int d = dr * dr + dg * dg + db * db;
339 | int index = i / 3;
340 | if (usedEntry[index] && (d < dmin)) {
341 | dmin = d;
342 | minpos = index;
343 | }
344 | i++;
345 | }
346 | return minpos;
347 | }
348 |
349 | /**
350 | * Extracts image pixels into byte array "pixels"
351 | */
352 | protected void getImagePixels() {
353 | int w = image.getWidth();
354 | int h = image.getHeight();
355 | int type = image.getType();
356 | if ((w != width)
357 | || (h != height)
358 | || (type != BufferedImage.TYPE_3BYTE_BGR)) {
359 | // create new image with right size/format
360 | BufferedImage temp =
361 | new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
362 | Graphics2D g = temp.createGraphics();
363 | g.drawImage(image, 0, 0, null);
364 | image = temp;
365 | }
366 | pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
367 | }
368 |
369 | /**
370 | * Writes Graphic Control Extension
371 | *
372 | * @throws IOException IO异常
373 | */
374 | protected void writeGraphicCtrlExt() throws IOException {
375 | out.write(0x21); // extension introducer
376 | out.write(0xf9); // GCE label
377 | out.write(4); // data block size
378 | int transp, disp;
379 | if (transparent == null) {
380 | transp = 0;
381 | disp = 0; // dispose = no action
382 | } else {
383 | transp = 1;
384 | disp = 2; // force clear if using transparent color
385 | }
386 | if (dispose >= 0) {
387 | disp = dispose & 7; // user override
388 | }
389 | disp <<= 2;
390 |
391 | // packed fields
392 | out.write(0 | // 1:3 reserved
393 | disp | // 4:6 disposal
394 | 0 | // 7 user input - 0 = none
395 | transp); // 8 transparency flag
396 |
397 | writeShort(delay); // delay x 1/100 sec
398 | out.write(transIndex); // transparent color index
399 | out.write(0); // block terminator
400 | }
401 |
402 | /**
403 | * Writes Image Descriptor
404 | *
405 | * @throws IOException IO异常
406 | */
407 | protected void writeImageDesc() throws IOException {
408 | out.write(0x2c); // image separator
409 | writeShort(0); // image position x,y = 0,0
410 | writeShort(0);
411 | writeShort(width); // image size
412 | writeShort(height);
413 | // packed fields
414 | if (firstFrame) {
415 | // no LCT - GCT is used for first (or only) frame
416 | out.write(0);
417 | } else {
418 | // specify normal LCT
419 | out.write(0x80 | // 1 local color table 1=yes
420 | 0 | // 2 interlace - 0=no
421 | 0 | // 3 sorted - 0=no
422 | 0 | // 4-5 reserved
423 | palSize); // 6-8 size of color table
424 | }
425 | }
426 |
427 | /**
428 | * Writes Logical Screen Descriptor
429 | *
430 | * @throws IOException IO异常
431 | */
432 | protected void writeLSD() throws IOException {
433 | // logical screen size
434 | writeShort(width);
435 | writeShort(height);
436 | // packed fields
437 | out.write((0x80 | // 1 : global color table flag = 1 (gct used)
438 | 0x70 | // 2-4 : color resolution = 7
439 | 0x00 | // 5 : gct sort flag = 0
440 | palSize)); // 6-8 : gct size
441 |
442 | out.write(0); // background color index
443 | out.write(0); // pixel aspect ratio - assume 1:1
444 | }
445 |
446 | /**
447 | * Writes Netscape application extension to define
448 | * repeat count.
449 | *
450 | * @throws IOException IO异常
451 | */
452 | protected void writeNetscapeExt() throws IOException {
453 | out.write(0x21); // extension introducer
454 | out.write(0xff); // app extension label
455 | out.write(11); // block size
456 | writeString("NETSCAPE" + "2.0"); // app id + auth code
457 | out.write(3); // sub-block size
458 | out.write(1); // loop sub-block id
459 | writeShort(repeat); // loop count (extra iterations, 0=repeat forever)
460 | out.write(0); // block terminator
461 | }
462 |
463 | /**
464 | * Writes color table
465 | *
466 | * @throws IOException IO异常
467 | */
468 | protected void writePalette() throws IOException {
469 | out.write(colorTab, 0, colorTab.length);
470 | int n = (3 * 256) - colorTab.length;
471 | for (int i = 0; i < n; i++) {
472 | out.write(0);
473 | }
474 | }
475 |
476 | /**
477 | * Encodes and writes pixel data
478 | *
479 | * @throws IOException IO异常
480 | */
481 | protected void writePixels() throws IOException {
482 | Encoder encoder = new Encoder(width, height, indexedPixels, colorDepth);
483 | encoder.encode(out);
484 | }
485 |
486 | /**
487 | * Write 16-bit value to output stream, LSB first
488 | *
489 | * @param value int
490 | * @throws IOException IO异常
491 | */
492 | protected void writeShort(int value) throws IOException {
493 | out.write(value & 0xff);
494 | out.write((value >> 8) & 0xff);
495 | }
496 |
497 | /**
498 | * Writes string to output stream
499 | *
500 | * @param s string
501 | * @throws IOException IO异常
502 | */
503 | protected void writeString(String s) throws IOException {
504 | for (int i = 0; i < s.length(); i++) {
505 | out.write((byte) s.charAt(i));
506 | }
507 | }
508 | }
509 |
--------------------------------------------------------------------------------
/src/main/java/com/wf/captcha/utils/Quant.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha.utils;
2 |
3 | /**
4 | *
5 | */
6 | public class Quant {
7 | protected static final int netsize = 256; /* number of colours used */
8 |
9 | /* four primes near 500 - assume no image has a length so large */
10 | /* that it is divisible by all four primes */
11 | protected static final int prime1 = 499;
12 | protected static final int prime2 = 491;
13 | protected static final int prime3 = 487;
14 | protected static final int prime4 = 503;
15 |
16 | protected static final int minpicturebytes = (3 * prime4);
17 | /* minimum size for input image */
18 |
19 | /* Program Skeleton
20 | ----------------
21 | [select samplefac in range 1..30]
22 | [read image from input file]
23 | pic = (unsigned char*) malloc(3*width*height);
24 | initnet(pic,3*width*height,samplefac);
25 | learn();
26 | unbiasnet();
27 | [write output image header, using writecolourmap(f)]
28 | inxbuild();
29 | write output image using inxsearch(b,g,r) */
30 |
31 | /* Network Definitions
32 | ------------------- */
33 |
34 | protected static final int maxnetpos = (netsize - 1);
35 | protected static final int netbiasshift = 4; /* bias for colour values */
36 | protected static final int ncycles = 100; /* no. of learning cycles */
37 |
38 | /* defs for freq and bias */
39 | protected static final int intbiasshift = 16; /* bias for fractions */
40 | protected static final int intbias = (((int) 1) << intbiasshift);
41 | protected static final int gammashift = 10; /* gamma = 1024 */
42 | protected static final int gamma = (((int) 1) << gammashift);
43 | protected static final int betashift = 10;
44 | protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */
45 | protected static final int betagamma =
46 | (intbias << (gammashift - betashift));
47 |
48 | /* defs for decreasing radius factor */
49 | protected static final int initrad = (netsize >> 3); /* for 256 cols, radius starts */
50 | protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
51 | protected static final int radiusbias = (((int) 1) << radiusbiasshift);
52 | protected static final int initradius = (initrad * radiusbias); /* and decreases by a */
53 | protected static final int radiusdec = 30; /* factor of 1/30 each cycle */
54 |
55 | /* defs for decreasing alpha factor */
56 | protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */
57 | protected static final int initalpha = (((int) 1) << alphabiasshift);
58 |
59 | protected int alphadec; /* biased by 10 bits */
60 |
61 | /* radbias and alpharadbias used for radpower calculation */
62 | protected static final int radbiasshift = 8;
63 | protected static final int radbias = (((int) 1) << radbiasshift);
64 | protected static final int alpharadbshift = (alphabiasshift + radbiasshift);
65 | protected static final int alpharadbias = (((int) 1) << alpharadbshift);
66 |
67 | /* Types and Global Variables
68 | -------------------------- */
69 |
70 | protected byte[] thepicture; /* the input image itself */
71 | protected int lengthcount; /* lengthcount = H*W*3 */
72 |
73 | protected int samplefac; /* sampling factor 1..30 */
74 |
75 | // typedef int pixel[4]; /* BGRc */
76 | protected int[][] network; /* the network itself - [netsize][4] */
77 |
78 | protected int[] netindex = new int[256];
79 | /* for network lookup - really 256 */
80 |
81 | protected int[] bias = new int[netsize];
82 | /* bias and freq arrays for learning */
83 | protected int[] freq = new int[netsize];
84 | protected int[] radpower = new int[initrad];
85 | /* radpower for precomputation */
86 |
87 | /* Initialise network in range (0,0,0) to (255,255,255) and set parameters
88 | ----------------------------------------------------------------------- */
89 | public Quant(byte[] thepic, int len, int sample) {
90 |
91 | int i;
92 | int[] p;
93 |
94 | thepicture = thepic;
95 | lengthcount = len;
96 | samplefac = sample;
97 |
98 | network = new int[netsize][];
99 | for (i = 0; i < netsize; i++) {
100 | network[i] = new int[4];
101 | p = network[i];
102 | p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
103 | freq[i] = intbias / netsize; /* 1/netsize */
104 | bias[i] = 0;
105 | }
106 | }
107 |
108 | public byte[] colorMap() {
109 | byte[] map = new byte[3 * netsize];
110 | int[] index = new int[netsize];
111 | for (int i = 0; i < netsize; i++)
112 | index[network[i][3]] = i;
113 | int k = 0;
114 | for (int i = 0; i < netsize; i++) {
115 | int j = index[i];
116 | map[k++] = (byte) (network[j][0]);
117 | map[k++] = (byte) (network[j][1]);
118 | map[k++] = (byte) (network[j][2]);
119 | }
120 | return map;
121 | }
122 |
123 | /* Insertion sort of network and building of netindex[0..255] (to do after unbias)
124 | ------------------------------------------------------------------------------- */
125 | public void inxbuild() {
126 |
127 | int i, j, smallpos, smallval;
128 | int[] p;
129 | int[] q;
130 | int previouscol, startpos;
131 |
132 | previouscol = 0;
133 | startpos = 0;
134 | for (i = 0; i < netsize; i++) {
135 | p = network[i];
136 | smallpos = i;
137 | smallval = p[1]; /* index on g */
138 | /* find smallest in i..netsize-1 */
139 | for (j = i + 1; j < netsize; j++) {
140 | q = network[j];
141 | if (q[1] < smallval) { /* index on g */
142 | smallpos = j;
143 | smallval = q[1]; /* index on g */
144 | }
145 | }
146 | q = network[smallpos];
147 | /* swap p (i) and q (smallpos) entries */
148 | if (i != smallpos) {
149 | j = q[0];
150 | q[0] = p[0];
151 | p[0] = j;
152 | j = q[1];
153 | q[1] = p[1];
154 | p[1] = j;
155 | j = q[2];
156 | q[2] = p[2];
157 | p[2] = j;
158 | j = q[3];
159 | q[3] = p[3];
160 | p[3] = j;
161 | }
162 | /* smallval entry is now in position i */
163 | if (smallval != previouscol) {
164 | netindex[previouscol] = (startpos + i) >> 1;
165 | for (j = previouscol + 1; j < smallval; j++)
166 | netindex[j] = i;
167 | previouscol = smallval;
168 | startpos = i;
169 | }
170 | }
171 | netindex[previouscol] = (startpos + maxnetpos) >> 1;
172 | for (j = previouscol + 1; j < 256; j++)
173 | netindex[j] = maxnetpos; /* really 256 */
174 | }
175 |
176 | /* Main Learning Loop
177 | ------------------ */
178 | public void learn() {
179 |
180 | int i, j, b, g, r;
181 | int radius, rad, alpha, step, delta, samplepixels;
182 | byte[] p;
183 | int pix, lim;
184 |
185 | if (lengthcount < minpicturebytes)
186 | samplefac = 1;
187 | alphadec = 30 + ((samplefac - 1) / 3);
188 | p = thepicture;
189 | pix = 0;
190 | lim = lengthcount;
191 | samplepixels = lengthcount / (3 * samplefac);
192 | delta = samplepixels / ncycles;
193 | alpha = initalpha;
194 | radius = initradius;
195 |
196 | rad = radius >> radiusbiasshift;
197 | if (rad <= 1)
198 | rad = 0;
199 | for (i = 0; i < rad; i++)
200 | radpower[i] =
201 | alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
202 |
203 | //fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);
204 |
205 | if (lengthcount < minpicturebytes)
206 | step = 3;
207 | else if ((lengthcount % prime1) != 0)
208 | step = 3 * prime1;
209 | else {
210 | if ((lengthcount % prime2) != 0)
211 | step = 3 * prime2;
212 | else {
213 | if ((lengthcount % prime3) != 0)
214 | step = 3 * prime3;
215 | else
216 | step = 3 * prime4;
217 | }
218 | }
219 |
220 | i = 0;
221 | while (i < samplepixels) {
222 | b = (p[pix + 0] & 0xff) << netbiasshift;
223 | g = (p[pix + 1] & 0xff) << netbiasshift;
224 | r = (p[pix + 2] & 0xff) << netbiasshift;
225 | j = contest(b, g, r);
226 |
227 | altersingle(alpha, j, b, g, r);
228 | if (rad != 0)
229 | alterneigh(rad, j, b, g, r); /* alter neighbours */
230 |
231 | pix += step;
232 | if (pix >= lim)
233 | pix -= lengthcount;
234 |
235 | i++;
236 | if (delta == 0)
237 | delta = 1;
238 | if (i % delta == 0) {
239 | alpha -= alpha / alphadec;
240 | radius -= radius / radiusdec;
241 | rad = radius >> radiusbiasshift;
242 | if (rad <= 1)
243 | rad = 0;
244 | for (j = 0; j < rad; j++)
245 | radpower[j] =
246 | alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
247 | }
248 | }
249 | //fprintf(stderr,"finished 1D learning: final alpha=%f !\n",((float)alpha)/initalpha);
250 | }
251 |
252 | /* Search for BGR values 0..255 (after net is unbiased) and return colour index
253 | ---------------------------------------------------------------------------- */
254 | public int map(int b, int g, int r) {
255 |
256 | int i, j, dist, a, bestd;
257 | int[] p;
258 | int best;
259 |
260 | bestd = 1000; /* biggest possible dist is 256*3 */
261 | best = -1;
262 | i = netindex[g]; /* index on g */
263 | j = i - 1; /* start at netindex[g] and work outwards */
264 |
265 | while ((i < netsize) || (j >= 0)) {
266 | if (i < netsize) {
267 | p = network[i];
268 | dist = p[1] - g; /* inx key */
269 | if (dist >= bestd)
270 | i = netsize; /* stop iter */
271 | else {
272 | i++;
273 | if (dist < 0)
274 | dist = -dist;
275 | a = p[0] - b;
276 | if (a < 0)
277 | a = -a;
278 | dist += a;
279 | if (dist < bestd) {
280 | a = p[2] - r;
281 | if (a < 0)
282 | a = -a;
283 | dist += a;
284 | if (dist < bestd) {
285 | bestd = dist;
286 | best = p[3];
287 | }
288 | }
289 | }
290 | }
291 | if (j >= 0) {
292 | p = network[j];
293 | dist = g - p[1]; /* inx key - reverse dif */
294 | if (dist >= bestd)
295 | j = -1; /* stop iter */
296 | else {
297 | j--;
298 | if (dist < 0)
299 | dist = -dist;
300 | a = p[0] - b;
301 | if (a < 0)
302 | a = -a;
303 | dist += a;
304 | if (dist < bestd) {
305 | a = p[2] - r;
306 | if (a < 0)
307 | a = -a;
308 | dist += a;
309 | if (dist < bestd) {
310 | bestd = dist;
311 | best = p[3];
312 | }
313 | }
314 | }
315 | }
316 | }
317 | return (best);
318 | }
319 |
320 | public byte[] process() {
321 | learn();
322 | unbiasnet();
323 | inxbuild();
324 | return colorMap();
325 | }
326 |
327 | /* Unbias network to give byte values 0..255 and record position i to prepare for sort
328 | ----------------------------------------------------------------------------------- */
329 | public void unbiasnet() {
330 |
331 | int i, j;
332 |
333 | for (i = 0; i < netsize; i++) {
334 | network[i][0] >>= netbiasshift;
335 | network[i][1] >>= netbiasshift;
336 | network[i][2] >>= netbiasshift;
337 | network[i][3] = i; /* record colour no */
338 | }
339 | }
340 |
341 | /* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
342 | --------------------------------------------------------------------------------- */
343 | protected void alterneigh(int rad, int i, int b, int g, int r) {
344 |
345 | int j, k, lo, hi, a, m;
346 | int[] p;
347 |
348 | lo = i - rad;
349 | if (lo < -1)
350 | lo = -1;
351 | hi = i + rad;
352 | if (hi > netsize)
353 | hi = netsize;
354 |
355 | j = i + 1;
356 | k = i - 1;
357 | m = 1;
358 | while ((j < hi) || (k > lo)) {
359 | a = radpower[m++];
360 | if (j < hi) {
361 | p = network[j++];
362 | try {
363 | p[0] -= (a * (p[0] - b)) / alpharadbias;
364 | p[1] -= (a * (p[1] - g)) / alpharadbias;
365 | p[2] -= (a * (p[2] - r)) / alpharadbias;
366 | } catch (Exception e) {
367 | } // prevents 1.3 miscompilation
368 | }
369 | if (k > lo) {
370 | p = network[k--];
371 | try {
372 | p[0] -= (a * (p[0] - b)) / alpharadbias;
373 | p[1] -= (a * (p[1] - g)) / alpharadbias;
374 | p[2] -= (a * (p[2] - r)) / alpharadbias;
375 | } catch (Exception e) {
376 | }
377 | }
378 | }
379 | }
380 |
381 | /* Move neuron i towards biased (b,g,r) by factor alpha
382 | ---------------------------------------------------- */
383 | protected void altersingle(int alpha, int i, int b, int g, int r) {
384 |
385 | /* alter hit neuron */
386 | int[] n = network[i];
387 | n[0] -= (alpha * (n[0] - b)) / initalpha;
388 | n[1] -= (alpha * (n[1] - g)) / initalpha;
389 | n[2] -= (alpha * (n[2] - r)) / initalpha;
390 | }
391 |
392 | /* Search for biased BGR values
393 | ---------------------------- */
394 | protected int contest(int b, int g, int r) {
395 |
396 | /* finds closest neuron (min dist) and updates freq */
397 | /* finds best neuron (min dist-bias) and returns position */
398 | /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
399 | /* bias[i] = gamma*((1/netsize)-freq[i]) */
400 |
401 | int i, dist, a, biasdist, betafreq;
402 | int bestpos, bestbiaspos, bestd, bestbiasd;
403 | int[] n;
404 |
405 | bestd = ~(((int) 1) << 31);
406 | bestbiasd = bestd;
407 | bestpos = -1;
408 | bestbiaspos = bestpos;
409 |
410 | for (i = 0; i < netsize; i++) {
411 | n = network[i];
412 | dist = n[0] - b;
413 | if (dist < 0)
414 | dist = -dist;
415 | a = n[1] - g;
416 | if (a < 0)
417 | a = -a;
418 | dist += a;
419 | a = n[2] - r;
420 | if (a < 0)
421 | a = -a;
422 | dist += a;
423 | if (dist < bestd) {
424 | bestd = dist;
425 | bestpos = i;
426 | }
427 | biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
428 | if (biasdist < bestbiasd) {
429 | bestbiasd = biasdist;
430 | bestbiaspos = i;
431 | }
432 | betafreq = (freq[i] >> betashift);
433 | freq[i] -= betafreq;
434 | bias[i] += (betafreq << gammashift);
435 | }
436 | freq[bestpos] += beta;
437 | bias[bestpos] -= betagamma;
438 | return (bestbiaspos);
439 | }
440 | }
--------------------------------------------------------------------------------
/src/main/resources/actionj.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/actionj.ttf
--------------------------------------------------------------------------------
/src/main/resources/epilog.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/epilog.ttf
--------------------------------------------------------------------------------
/src/main/resources/fresnel.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/fresnel.ttf
--------------------------------------------------------------------------------
/src/main/resources/headache.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/headache.ttf
--------------------------------------------------------------------------------
/src/main/resources/lexo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/lexo.ttf
--------------------------------------------------------------------------------
/src/main/resources/prefix.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/prefix.ttf
--------------------------------------------------------------------------------
/src/main/resources/progbot.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/progbot.ttf
--------------------------------------------------------------------------------
/src/main/resources/ransom.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/ransom.ttf
--------------------------------------------------------------------------------
/src/main/resources/robot.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/robot.ttf
--------------------------------------------------------------------------------
/src/main/resources/scandal.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ele-admin/EasyCaptcha/f6cb6062f55704661adffe8ad571bfff7412fc52/src/main/resources/scandal.ttf
--------------------------------------------------------------------------------
/src/test/java/com/wf/captcha/CaptchaTest.java:
--------------------------------------------------------------------------------
1 | package com.wf.captcha;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 |
8 | /**
9 | * 测试类
10 | * Created by 王帆 on 2018-07-27 上午 10:08.
11 | */
12 | public class CaptchaTest {
13 |
14 | @Test
15 | public void test() throws Exception {
16 | /*for (int i = 0; i < 10; i++) {
17 | SpecCaptcha specCaptcha = new SpecCaptcha();
18 | specCaptcha.setLen(4);
19 | specCaptcha.setFont(i, 32f);
20 | System.out.println(specCaptcha.text());
21 | specCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".png")));
22 | }*/
23 | }
24 |
25 | @Test
26 | public void testGIf() throws Exception {
27 | /*for (int i = 0; i < 10; i++) {
28 | GifCaptcha gifCaptcha = new GifCaptcha();
29 | gifCaptcha.setLen(5);
30 | gifCaptcha.setFont(i, 32f);
31 | System.out.println(gifCaptcha.text());
32 | gifCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".gif")));
33 | }*/
34 | }
35 |
36 | @Test
37 | public void testHan() throws Exception {
38 | /*for (int i = 0; i < 10; i++) {
39 | ChineseCaptcha chineseCaptcha = new ChineseCaptcha();
40 | System.out.println(chineseCaptcha.text());
41 | chineseCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".png")));
42 | }*/
43 | }
44 |
45 | @Test
46 | public void testGifHan() throws Exception {
47 | /*for (int i = 0; i < 10; i++) {
48 | ChineseGifCaptcha chineseGifCaptcha = new ChineseGifCaptcha();
49 | System.out.println(chineseGifCaptcha.text());
50 | chineseGifCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".gif")));
51 | }*/
52 | }
53 |
54 | @Test
55 | public void testArit() throws Exception {
56 | /*for (int i = 0; i < 10; i++) {
57 | ArithmeticCaptcha specCaptcha = new ArithmeticCaptcha();
58 | specCaptcha.setLen(3);
59 | specCaptcha.setFont(i, 28f);
60 | System.out.println(specCaptcha.getArithmeticString() + " " + specCaptcha.text());
61 | specCaptcha.out(new FileOutputStream(new File("C:/Java/aa" + i + ".png")));
62 | }*/
63 | }
64 |
65 | @Test
66 | public void testBase64() throws Exception {
67 | /*GifCaptcha specCaptcha = new GifCaptcha();
68 | System.out.println(specCaptcha.toBase64(""));*/
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------