use matrix transformation to replace individual transform
replace unneccessary space update readme
This commit is contained in:
parent
d573f66f82
commit
39397d0646
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Weilin Shi
|
||||
Copyright (c) 2016 - present Weilin Shi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
30
README.md
30
README.md
|
@ -9,11 +9,35 @@ generate svg captcha in node.js
|
|||
- cannot or do not want to use google recaptcha
|
||||
- have issue with install c++ addon
|
||||
|
||||
## usage
|
||||
```
|
||||
var svgCaptcha = require('svg-captcha');
|
||||
// generate random text of length 4
|
||||
var text = svgCaptcha.randomText();
|
||||
// generate svg image
|
||||
var captcha = svgCaptcha(text);
|
||||
```
|
||||
with express
|
||||
```
|
||||
var svgCaptcha = require('svg-captcha');
|
||||
|
||||
app.get('/captcha', function (req, res) {
|
||||
var text = svgCaptcha.randomText();
|
||||
var captcha = svgCaptcha(text);
|
||||
req.session.captcha = text;
|
||||
|
||||
res.set('Content-Type', 'image/svg+xml');
|
||||
res.status(200).send(captcha);
|
||||
});
|
||||
```
|
||||
|
||||
## sample image
|
||||
![image](media/example.svg)
|
||||
|
||||
## why use svg?
|
||||
|
||||
My colleage and I have issue installing c++ addon on windows and we don't want to mess with python and visual studio.
|
||||
Initially we thought that using svg will result in larger file,
|
||||
but it turns out that it's smaller than the jpeg captcha.
|
||||
It does not require any c++ addon.
|
||||
It uses opentype.js underneath and the result image is smaller than jpeg image.
|
||||
|
||||
## Translations
|
||||
[中文](README_CN.md)
|
||||
|
|
30
README_CN.md
30
README_CN.md
|
@ -9,11 +9,35 @@
|
|||
- 无法使用 google recaptcha
|
||||
- 无法安装 c++ 模块
|
||||
|
||||
## 使用方法
|
||||
```
|
||||
var svgCaptcha = require('svg-captcha');
|
||||
// generate random text of length 4
|
||||
var text = svgCaptcha.randomText();
|
||||
// generate svg image
|
||||
var captcha = svgCaptcha(text);
|
||||
```
|
||||
在 express中使用
|
||||
```
|
||||
var svgCaptcha = require('svg-captcha');
|
||||
|
||||
app.get('/captcha', function (req, res) {
|
||||
var text = svgCaptcha.randomText();
|
||||
var captcha = svgCaptcha(text);
|
||||
req.session.captcha = text;
|
||||
|
||||
res.set('Content-Type', 'image/svg+xml');
|
||||
res.status(200).send(captcha);
|
||||
});
|
||||
```
|
||||
|
||||
## 图片示例
|
||||
![image](media/example.svg)
|
||||
|
||||
## 为什么使用 svg 格式?
|
||||
|
||||
我和我的同事都是用windows系统,不想使用 c++ 模块。
|
||||
但现有的验证码生成都是基于 c++ 的。
|
||||
svg 格式的验证码竟然比 jpg 格式的还要小。
|
||||
不需要引用 c++ 模块。
|
||||
使用 opentype.js了,而且svg图片比jpeg格式图片要小
|
||||
|
||||
## Translations
|
||||
[中文](README_CN.md)
|
||||
|
|
27
index.js
27
index.js
|
@ -4,7 +4,7 @@ const textToSVG = require('text-to-svg').loadSync();
|
|||
const random = require('./random');
|
||||
|
||||
const generateBackground = function (width, height) {
|
||||
const seed = random.int(0, 1010101010);
|
||||
const seed = random.int(0, 10);
|
||||
|
||||
return `<filter id="n" x="0" y="0">
|
||||
<feTurbulence baseFrequency=".7,.07" seed="${seed}"/>
|
||||
|
@ -34,33 +34,28 @@ const getLineNoise = function (lv, width, height) {
|
|||
return noiseString.join('');
|
||||
};
|
||||
|
||||
const getSVGOptions = function (width, height) {
|
||||
const getSVGOptions = function (x, width, height) {
|
||||
return {
|
||||
x: 0, y: height / 2, fontSize: Math.floor(height * 0.72),
|
||||
anchor: 'left middle',
|
||||
x: x, y: height / 2, fontSize: Math.floor(height * 0.72),
|
||||
anchor: 'center middle',
|
||||
attributes: {fill: 'red', stroke: 'black'}
|
||||
};
|
||||
};
|
||||
|
||||
const getText = function (text, width, height) {
|
||||
const toSVGOptions = getSVGOptions(width, height);
|
||||
const len = text.length;
|
||||
const spacing = (width - 10) / (len + 1);
|
||||
const spacing = (width - 2) / (len + 1);
|
||||
var i = -1;
|
||||
var out = [];
|
||||
|
||||
|
||||
while (++i < len) {
|
||||
var charPath = textToSVG.getD(text[i], toSVGOptions);
|
||||
var charPath = textToSVG.getD(text[i],
|
||||
getSVGOptions((i + 1) * spacing, width, height));
|
||||
// randomly scale it to 95% - 105%, skew
|
||||
var randomScale = random.int(95, 105) / 100;
|
||||
var randomTranslateX = (i + 1) * spacing + random.int(-2, 2);
|
||||
var randomTranslateY = random.int(-3, 3);
|
||||
var randomMatrix = random.matrix();
|
||||
var color = random.greyColor(0, 4);
|
||||
var randomSkewX = random.int(-7, 7);
|
||||
out.push(`<path fill="${color}" d="${charPath}"
|
||||
transform="scale(${randomScale})
|
||||
translate(${randomTranslateX},${randomTranslateY})
|
||||
skewX(${randomSkewX})"/>`);
|
||||
transform="matrix(${randomMatrix})"/>`);
|
||||
}
|
||||
|
||||
return out.join('');
|
||||
|
@ -86,7 +81,7 @@ const createCaptcha = function (options) {
|
|||
${bg}
|
||||
</svg>`;
|
||||
|
||||
return xml.replace('\t', '');
|
||||
return xml.replace(/[\t]/g, '').replace(/\n(\W)/g, '$1');
|
||||
};
|
||||
|
||||
module.exports = createCaptcha;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="150" height="50"><path fill="#333" d="M25.26 22.19L27.05 22.19Q29.35 22.19 30.21 21.54Q31.83 20.29 31.83 17.79Q31.83 13.36 27.81 13.36Q24.47 13.36 23.71 17.14L20.86 17.14Q21.25 14.77 22.52 13.22Q24.45 10.92 27.81 10.92Q30.62 10.92 32.45 12.54Q34.61 14.44 34.61 17.69Q34.61 22.06 30.69 23.42Q35.42 25.25 35.42 30.08Q35.42 33.17 33.66 35.16Q31.55 37.57 27.86 37.57Q24.40 37.57 22.34 35.20Q20.83 33.46 20.51 30.40L23.46 30.40Q23.83 35.09 27.86 35.09Q29.72 35.09 30.99 34.04Q32.57 32.68 32.57 30.08Q32.57 24.49 27.05 24.49L25.26 24.49L25.26 22.19Z"
|
||||
transform="matrix(0.99 0 -0.1227845609029046 0.99 -1 0)"/><path fill="#333" d="M52.01 31.54Q52.48 35.14 56.21 35.14Q59.96 35.14 59.96 32.51Q59.96 31.24 59.09 30.54Q58.18 29.78 55.82 28.96L55.31 28.76Q52.71 27.87 51.54 26.97Q49.85 25.65 49.85 23.79Q49.85 21.55 51.80 20.20Q53.49 19.04 55.89 19.04Q61.33 19.04 62.19 23.98L59.39 23.98Q58.95 21.34 55.86 21.34Q52.55 21.34 52.55 23.68Q52.55 25.21 56.98 26.78Q59.48 27.65 60.57 28.45Q62.70 29.96 62.70 32.44Q62.70 34.84 60.80 36.25Q59.01 37.55 56.14 37.55Q49.87 37.55 49.16 31.54L52.01 31.54Z"
|
||||
transform="matrix(1.02 0 -0.2679491924311227 1.02 -1 2)"/><path fill="#444" d="M77.85 11.64L80.52 11.64L80.52 27.09L86.81 19.74L90.17 19.74L84.74 25.83L91.58 36.85L88.22 36.85L83.07 27.72L80.52 30.47L80.52 36.85L77.85 36.85L77.85 11.64Z"
|
||||
transform="matrix(1.01 0 -0.14054083470239145 1.01 -1 -3)"/><path fill="#444" d="M104.83 19.74L107.85 19.74L112 33.56L116.13 19.74L119.15 19.74L113.48 36.85L110.52 36.85L104.83 19.74Z"
|
||||
transform="matrix(0.98 0 -0.0524077792830412 0.98 -1 1)"/><path d="M12 16 C66 36,94 17,140 26"
|
||||
stroke="#888" fill="transparent"/><path d="M11 13 C98 22,97 13,130 38"
|
||||
stroke="#888" fill="transparent"/><path d="M17 26 C58 39,78 24,143 39"
|
||||
stroke="#333" fill="transparent"/><filter id="n" x="0" y="0"><feTurbulence baseFrequency=".7,.07" seed="145"/><feColorMatrix type="luminanceToAlpha"/></filter><rect width="150" height="50" filter="url(#n)" opacity="0.2"/></svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "svg-captcha",
|
||||
"version": "0.9.2",
|
||||
"version": "0.9.4",
|
||||
"description": "generate svg captcha in node.js or express.js",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
13
random.js
13
random.js
|
@ -16,13 +16,24 @@ exports.greyColor = function (min, max) {
|
|||
return '#' + int + int + int;
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en/docs/Web/SVG/Attribute/transform
|
||||
exports.matrix = function () {
|
||||
var scale = randomInt(95, 105) / 100;
|
||||
var dx = randomInt(-2, 2);
|
||||
var dy = randomInt(-3, 3);
|
||||
// - 15 to 15 deg
|
||||
var skewX = randomInt(-267, 267) / 1000;
|
||||
|
||||
return `${scale} 0 ${skewX} ${scale} ${dx} ${dy}`;
|
||||
};
|
||||
|
||||
exports.captchaText = function (size) {
|
||||
size = size || 4;
|
||||
var i = -1;
|
||||
var out = '';
|
||||
|
||||
while (++i < size) {
|
||||
out += presets[randomInt(0, 61)]
|
||||
out += presets[randomInt(0, 61)];
|
||||
}
|
||||
|
||||
return out;
|
||||
|
|
|
@ -2,7 +2,7 @@ const fs = require('fs');
|
|||
const svg = require('./');
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
fs.writeFile(`test${i}.svg`, svg('d3e4'), 'utf8', (err) => {
|
||||
fs.writeFile(`test${i}.svg`, svg(svg.randomText()), 'utf8', (err) => {
|
||||
if (err) console.error(err)
|
||||
else console.log('it\'s saved');
|
||||
});
|
||||
|
|
3
test.js
3
test.js
|
@ -1,10 +1,13 @@
|
|||
const assert = require('assert');
|
||||
const svgCaptcha = require('./');
|
||||
|
||||
console.time('generate 50 images');
|
||||
for (var i = 0; i < 50; i++) {
|
||||
var text = svgCaptcha.randomText();
|
||||
assert(/^[0-9a-zA-Z]+$/.test(text));
|
||||
svgCaptcha(text);
|
||||
}
|
||||
console.timeEnd('generate 50 images');
|
||||
|
||||
const xmlReg = /^<svg[\s\S]+\/svg>$/;
|
||||
|
||||
|
|
Loading…
Reference in New Issue