captchaGen.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // Использование этого модуля - см. строку 349
  2. "use strict";
  3. /**
  4. * A handy class to calculate color values
  5. * © 2013, Robert Eisele robert@xarg.org
  6. * Modified 2013, by George Chan gchan@21cn.com
  7. * Modified 2019, Александр Гольдин a@goldin.su
  8. * http://www.opensource.org/licenses/bsd-license.php BSD License
  9. */
  10. function pnglib(width, height, depth) {
  11. function write(buffer, offs) {
  12. for (let i = 2; i < arguments.length; i++) {
  13. for (let j = 0; j < arguments[i].length; j++) {
  14. buffer[offs++] = arguments[i].charAt(j);
  15. }
  16. }
  17. }
  18. function byte2(w) {
  19. return String.fromCharCode((w >> 8) & 255, w & 255);
  20. }
  21. function byte4(w) {
  22. return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
  23. }
  24. function byte2lsb(w) {
  25. return String.fromCharCode(w & 255, (w >> 8) & 255);
  26. }
  27. this.width = width;
  28. this.height = height;
  29. this.depth = depth;
  30. this.pix_size = height * (width + 1);
  31. this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
  32. this.ihdr_offs = 0;
  33. this.ihdr_size = 4 + 4 + 13 + 4;
  34. this.plte_offs = this.ihdr_offs + this.ihdr_size;
  35. this.plte_size = 4 + 4 + 3 * depth + 4;
  36. this.trns_offs = this.plte_offs + this.plte_size;
  37. this.trns_size = 4 + 4 + depth + 4;
  38. this.idat_offs = this.trns_offs + this.trns_size;
  39. this.idat_size = 4 + 4 + this.data_size + 4;
  40. this.iend_offs = this.idat_offs + this.idat_size;
  41. this.iend_size = 4 + 4 + 4;
  42. this.buffer_size = this.iend_offs + this.iend_size;
  43. this.buffer = new Array();
  44. this.palette = new Object();
  45. this.pindex = 0;
  46. let _crc32 = new Array();
  47. for (let i = 0; i < this.buffer_size; i++) {
  48. this.buffer[i] = "\x00";
  49. }
  50. write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
  51. write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
  52. write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
  53. write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
  54. write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
  55. let header = ((8 + (7 << 4)) << 8) | (3 << 6);
  56. header+= 31 - (header % 31);
  57. write(this.buffer, this.idat_offs + 8, byte2(header));
  58. for (let i = 0; (i << 16) - 1 < this.pix_size; i++) {
  59. let size, bits;
  60. if (i + 0xffff < this.pix_size) {
  61. size = 0xffff;
  62. bits = "\x00";
  63. } else {
  64. size = this.pix_size - (i << 16) - i;
  65. bits = "\x01";
  66. }
  67. write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
  68. }
  69. for (let i = 0; i < 256; i++) {
  70. let c = i;
  71. for (let j = 0; j < 8; j++) {
  72. if (c & 1) {
  73. c = -306674912 ^ ((c >> 1) & 0x7fffffff);
  74. } else {
  75. c = (c >> 1) & 0x7fffffff;
  76. }
  77. }
  78. _crc32[i] = c;
  79. }
  80. this.index = function(x,y) {
  81. let i = y * (this.width + 1) + x + 1;
  82. let j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
  83. return j;
  84. }
  85. this.color = function(red, green, blue, alpha) {
  86. alpha = alpha >= 0 ? alpha : 255;
  87. let color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
  88. if (typeof this.palette[color] == "undefined") {
  89. if (this.pindex == this.depth) return "\x00";
  90. let ndx = this.plte_offs + 8 + 3 * this.pindex;
  91. this.buffer[ndx + 0] = String.fromCharCode(red);
  92. this.buffer[ndx + 1] = String.fromCharCode(green);
  93. this.buffer[ndx + 2] = String.fromCharCode(blue);
  94. this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
  95. this.palette[color] = String.fromCharCode(this.pindex++);
  96. }
  97. return this.palette[color];
  98. }
  99. this.getBase64 = function() {
  100. let s = this.getDump();
  101. let ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  102. let c1, c2, c3, e1, e2, e3, e4;
  103. let l = s.length;
  104. let i = 0;
  105. let r = "";
  106. do {
  107. c1 = s.charCodeAt(i);
  108. e1 = c1 >> 2;
  109. c2 = s.charCodeAt(i+1);
  110. e2 = ((c1 & 3) << 4) | (c2 >> 4);
  111. c3 = s.charCodeAt(i+2);
  112. if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
  113. if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; }
  114. r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
  115. } while ((i+= 3) < l);
  116. return r;
  117. }
  118. this.getDump = function() {
  119. let BASE = 65521;
  120. let NMAX = 5552;
  121. let s1 = 1;
  122. let s2 = 0;
  123. let n = NMAX;
  124. for (let y = 0; y < this.height; y++) {
  125. for (let x = -1; x < this.width; x++) {
  126. s1+= this.buffer[this.index(x, y)].charCodeAt(0);
  127. s2+= s1;
  128. if ((n-= 1) == 0) {
  129. s1%= BASE;
  130. s2%= BASE;
  131. n = NMAX;
  132. }
  133. }
  134. }
  135. s1%= BASE;
  136. s2%= BASE;
  137. write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
  138. function crc32(png, offs, size) {
  139. let crc = -1;
  140. for (let i = 4; i < size-4; i += 1) {
  141. crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
  142. }
  143. write(png, offs+size-4, byte4(crc ^ -1));
  144. }
  145. crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
  146. crc32(this.buffer, this.plte_offs, this.plte_size);
  147. crc32(this.buffer, this.trns_offs, this.trns_size);
  148. crc32(this.buffer, this.idat_offs, this.idat_size);
  149. crc32(this.buffer, this.iend_offs, this.iend_size);
  150. return "\u0089PNG\r\n\u001A\n"+this.buffer.join('');
  151. }
  152. };
  153. /**
  154. * Captcha PNG generator
  155. * © 2013, George Chan gchan@21cn.com
  156. * Modified 2019, Александр Гольдин a@goldin.su
  157. * http://www.opensource.org/licenses/bsd-license.php BSD License
  158. */
  159. this.numMask = [];
  160. this.numMask[0]=[];
  161. this.numMask[0]=loadNumMask0();
  162. this.numMask[1]=loadNumMask1();
  163. let myself = this;
  164. function loadNumMask0() {
  165. let numbmp=[];
  166. numbmp[0]=[
  167. "0011111000", "0111111110", "0111111110", "1110001111", "1110001111",
  168. "1110001111", "1110001111", "1110001111", "1110001111", "1110001111",
  169. "1110001111", "1110001111", "1110001111", "1110001111", "1110001111",
  170. "1110001111", "0111111111", " 111111110", "0011111100"];
  171. numbmp[1]=[
  172. "0000011", "0000111", "0011111", "1111111", "1111111", "0001111",
  173. "0001111", "0001111", "0001111", "0001111", "0001111", "0001111",
  174. "0001111", "0001111", "0001111", "0001111", "0001111", "0001111",
  175. "0001111"];
  176. numbmp[2]=[
  177. "001111100", "011111110", "111111111", "111001111", "111001111",
  178. "111001111", "111001111", "000011111", "000011110", "000111110",
  179. "000111100", "000111100", "001111000", "001111000", "011110000",
  180. "011110000", "111111111", "111111111", "111111111"];
  181. numbmp[3]=[
  182. "0011111100", "0111111110", "1111111111", "1111001111", "1111001111",
  183. "1111001111", "0000001111", "0001111110", "0001111100", "0001111111",
  184. "0000001111", "1111001111", "1111001111", "1111001111", "1111001111",
  185. "1111001111", "1111111111", "0111111110", "0011111100"];
  186. numbmp[4]=[
  187. "00001111110", "00001111110", "00011111110", "00011111110", "00011111110",
  188. "00111011110", "00111011110", "00111011110", "01110011110", "01110011110",
  189. "01110011110", "11100011110", "11111111111", "11111111111", "11111111111",
  190. "11111111111", "00000011110", "00000011110", "00000011110"];
  191. numbmp[5]=[
  192. "1111111111", "1111111111", "1111111111", "1111000000", "1111000000",
  193. "1111011100", "1111111110", "1111111111", "1111001111", "1111001111",
  194. "0000001111", "0000001111", "1111001111", "1111001111", "1111001111",
  195. "1111001111", "1111111111", "0111111110", "0011111100"];
  196. numbmp[6]=[
  197. "0011111100", "0111111110", "0111111111", "1111001111", "1111001111",
  198. "1111000000", "1111011100", "1111111110", "1111111111", "1111001111",
  199. "1111001111", "1111001111", "1111001111", "1111001111", "1111001111",
  200. "1111001111", "0111111111", "0111111110", "0011111100"];
  201. numbmp[7]=[
  202. "11111111", "11111111", "11111111", "00001111", "00001111", "00001111",
  203. "00001110", "00001110", "00011110", "00011110", "00011110", "00011100",
  204. "00111100", "00111100", "00111100", "00111100", "00111000", "01111000",
  205. "01111000"];
  206. numbmp[8]=[
  207. "0011111100", "0111111110", "1111111111", "1111001111", "1111001111",
  208. "1111001111", "1111001111", "0111111110", "0011111100", "0111111110",
  209. "1111001111", "1111001111", "1111001111", "1111001111", "1111001111",
  210. "1111001111", "1111111111", "0111111110", "0011111100"];
  211. numbmp[9]=[
  212. "0011111100", "0111111110", "1111111111", "1111001111", "1111001111",
  213. "1111001111", "1111001111", "1111001111", "1111001111", "1111001111",
  214. "1111111111", "0111111111", "0011101111", "0000001111", "1111001111",
  215. "1111001111", "1111111110", "0111111110", "0011111000"];
  216. return numbmp;
  217. }
  218. function loadNumMask1() {
  219. let numbmp=[];
  220. numbmp[0] = [
  221. "000000001111000", "000000111111110", "000001110000110", "000011000000011",
  222. "000110000000011", "001100000000011", "011100000000011", "011000000000011",
  223. "111000000000110", "110000000000110", "110000000001110", "110000000001100",
  224. "110000000011000", "110000000111000", "011000011110000", "011111111000000",
  225. "000111110000000"];
  226. numbmp[1] = [
  227. "00000111", "00001111", "00011110", "00010110", "00001100", "00001100",
  228. "00011000", "00011000", "00110000", "00110000", "00110000", "01100000",
  229. "01100000", "01100000", "11000000", "11000000", "11000000"];
  230. numbmp[2] = [
  231. "00000011111000", "00001111111110", "00011100000110", "00011000000011",
  232. "00000000000011", "00000000000011", "00000000000011", "00000000000110",
  233. "00000000001110", "00000000011100", "00000001110000", "00000111100000",
  234. "00001110000000", "00111100000000", "01110000000000", "11111111110000",
  235. "11111111111110", "00000000011110"];
  236. numbmp[3] = [
  237. "000000111111000", "000011111111110", "000111100000111", "000110000000011",
  238. "000000000000011", "000000000000011", "000000000001110", "000000111111000",
  239. "000000111111000", "000000000011100", "000000000001100", "000000000001100",
  240. "110000000001100", "111000000011100", "111100000111000", "001111111110000",
  241. "000111111000000"];
  242. numbmp[4] = [
  243. "00000011000001", "00000110000011", "00001100000010", "00011000000110",
  244. "00111000000110", "00110000001100", "01100000001100", "01100000001000",
  245. "11000000011000", "11111111111111", "11111111111111", "00000000110000",
  246. "00000000110000", "00000000100000", "00000001100000", "00000001100000",
  247. "00000001100000"];
  248. numbmp[5] = [
  249. "0000001111111111", "0000011111111111", "0000111000000000", "0000110000000000",
  250. "0000110000000000", "0001110000000000", "0001101111100000", "0001111111111000",
  251. "0001110000011000", "0000000000001100", "0000000000001100", "0000000000001100",
  252. "1100000000001100", "1110000000011000", "1111000001111000", "0111111111100000",
  253. "0001111110000000"];
  254. numbmp[6] = [
  255. "000000001111100", "000000111111110", "000011110000111", "000111000000011", "000110000000000", "001100000000000", "011001111100000", "011111111111000",
  256. "111110000011000", "111000000001100", "110000000001100", "110000000001100",
  257. "110000000001100", "111000000011000", "011100001110000", "001111111100000",
  258. "000111110000000"];
  259. numbmp[7] = [
  260. "1111111111111", "1111111111111", "0000000001110", "0000000011100",
  261. "0000000111000", "0000000110000", "0000001100000", "0000011100000",
  262. "0000111000000", "0000110000000", "0001100000000", "0011100000000",
  263. "0011000000000", "0111000000000", "1110000000000", "1100000000000",
  264. "1100000000000"];
  265. numbmp[8] = [
  266. "0000000111110000", "0000011111111100", "0000011000001110", "0000110000000111",
  267. "0000110000011111", "0000110001111000", "0000011111100000", "0000011110000000",
  268. "0001111111000000", "0011100011100000", "0111000001110000", "1110000000110000",
  269. "1100000000110000", "1100000001110000", "1110000011100000", "0111111111000000",
  270. "0001111100000000"];
  271. numbmp[9] = [
  272. "0000011111000", "0001111111110", "0011100000110", "0011000000011",
  273. "0110000000011", "0110000000011", "0110000000011", "0110000000111",
  274. "0011000011110", "0011111111110", "0000111100110", "0000000001100",
  275. "0000000011000", "0000000111000", "0000011110000", "1111111000000",
  276. "1111110000000"];
  277. return numbmp;
  278. }
  279. function captchapng(width, height, dispNumber) {
  280. this.width = width;
  281. this.height = height;
  282. this.depth = 8;
  283. this.dispNumber = '' + dispNumber.toString();
  284. this.widthAverage = parseInt(this.width/this.dispNumber.length);
  285. let p = new pnglib(this.width,this.height,this.depth);
  286. for (let numSection = 0; numSection < this.dispNumber.length; numSection++) {
  287. let dispNum = this.dispNumber[numSection].valueOf();
  288. let font = parseInt(Math.random()*myself.numMask.length);
  289. font = (font >= myself.numMask.length ? 0 : font);
  290. let random_x_offs = parseInt(Math.random()*(this.widthAverage - myself.numMask[font][dispNum][0].length));
  291. let random_y_offs = parseInt(Math.random()*(this.height - myself.numMask[font][dispNum].length));
  292. random_x_offs = (random_x_offs < 0 ? 0 : random_x_offs);
  293. random_y_offs = (random_y_offs < 0 ? 0 : random_y_offs);
  294. for (let i=0;(i<myself.numMask[font][dispNum].length) && ((i+random_y_offs)<this.height);i++){
  295. let lineIndex = p.index(this.widthAverage * numSection + random_x_offs,i+random_y_offs);
  296. for (let j = 0; j < myself.numMask[font][dispNum][i].length; j++){
  297. if (
  298. (myself.numMask[font][dispNum][i][j] == '1') &&
  299. (this.widthAverage * numSection + random_x_offs+j) < this.width
  300. ) p.buffer[lineIndex+j] = '\x01';
  301. }
  302. }
  303. }
  304. return p;
  305. }
  306. /**
  307. * Генерирование капчи высотой 30px из числа num
  308. * © 2019, Александр Гольдин a@goldin.su
  309. *
  310. * Пример аргумента opt:
  311. * let opt = {
  312. * bkR: 246, bkG: 243, bkB: 240, // фоновый цвет
  313. * fnR: 214, fnG: 191, fnB: 168, // цвет шрифта
  314. * }
  315. */
  316. module.exports = function (num, opt) {
  317. let w = num.toString().length * 20;
  318. let capt = new captchapng(w, 30, num);
  319. capt.color(opt.bkR, opt.bkG, opt.bkB, 255);
  320. capt.color(opt.fnR, opt.fnG, opt.fnB, 255);
  321. let result = capt.getBase64();
  322. return new Buffer.from(result, "base64");
  323. }