add support for font option and multiple fonts

This commit is contained in:
HF 2021-08-21 16:15:56 +02:00
parent d557f8a42f
commit d73f3d4c9e
5 changed files with 60 additions and 50 deletions

View File

@ -63,7 +63,7 @@ app.get('/captcha', function (req, res) {
## API
#### `svgCaptcha.create(options)`
If no option is passed, you will get a random string of four characters and corresponding svg.
Get a random string of four characters and corresponding svg captcha.
* `size`: 4 // size of random string
* `ignoreChars`: '0o1i' // filter out some characters like 0o1i
@ -71,6 +71,7 @@ If no option is passed, you will get a random string of four characters and corr
* `stroke`: 'black' // style/color of the svg path stroke
* `fill`: 'black' // style/color of the svg strokes fill
* style: '' // svg path style attribute, i.e.: 'stroke-width: 4;'
* `font`: Font object (see svgCaptcha.loadFont) or Array of multiple Font objects
Options that change the variation of the path:
@ -97,12 +98,17 @@ This function returns an object that has the following property:
* `data`: string // svg of the math expression
* `text`: string // the answer of the math expression
#### `svgCaptcha.createCaptcha(text, options)`
return a svg captcha based on text provided.
Options same as create.
Returns svg string.
#### `svgCaptcha.loadFont(url)`
Load your own font and override the default font.
Load your own font.
* `url`: string // path to your font
This api is a wrapper around loadFont api of opentype.js.
Returns a Font object which you can use in options.
(this api is a wrapper around loadFont api of opentype.js)
Your may need experiment around various options to make your own font accessible.
See the following api.
#### `svgCaptcha.options`
Gain access to global setting object.
@ -114,11 +120,6 @@ In addition to size, color, and background, you can also set the following prope
* `fontSize`: number // captcha text size
* `charPreset`: string // random character preset
#### `svgCaptcha.randomText([size|options])`
return a random string.
#### `svgCaptcha(text, options)`
return a svg captcha based on text provided.
In pre 1.1.0 version you have to call these two functions,
now you can call create() to save some key strokes ;).

View File

@ -6,13 +6,16 @@ module.exports = function (text, opts) {
assert(ch, 'expect a string');
const fontSize = opts.fontSize;
const fontScale = fontSize / opts.font.unitsPerEm;
const font = (Array.isArray(opts.font))
? opts.font[Math.floor(Math.random() * opts.font.length)]
: opts.font;
const fontScale = fontSize / font.unitsPerEm;
const glyph = opts.font.charToGlyph(ch);
const glyph = font.charToGlyph(ch);
const width = glyph.advanceWidth ? glyph.advanceWidth * fontScale : 0;
const left = opts.x - (width / 2);
const height = (opts.ascender + opts.descender) * fontScale;
const height = (font.ascender + font.descender) * fontScale;
const top = opts.y + (height / 2);
const path = glyph.getPath(left, top, fontSize);

View File

@ -6,6 +6,31 @@ const optionMngr = require('./option-manager');
const opts = optionMngr.options;
function shuffleArray(array) {
let count = array.length,
randomnumber,
temp;
while( count ){
randomnumber = Math.random() * count-- | 0;
temp = array[count];
array[count] = array[randomnumber];
array[randomnumber] = temp
}
}
function mergePaths(paths) {
if (!paths.length) {
return [];
}
const out = paths[0];
for (let i = 1; i < paths.length; i += 1) {
out.commands = out.commands.concat(
paths[i].commands,
);
}
return out;
}
const getTextPath = function (text, width, height, options) {
const len = text.length;
const spacing = (width - 2) / (len + 1);
@ -22,22 +47,7 @@ const getTextPath = function (text, width, height, options) {
return out;
};
function mergePaths(paths) {
if (!paths.length) {
return [];
}
const out = paths[0];
for (let i = 1; i < paths.length; i += 1) {
out.commands = out.commands.concat(
paths[i].commands,
);
}
return out;
}
const createCaptcha = function (text, options) {
text = text || random.captchaText();
options = Object.assign({}, opts, options);
const width = options.width;
const height = options.height;
const bg = options.background;
@ -46,9 +56,8 @@ const createCaptcha = function (text, options) {
`<rect width="100%" height="100%" fill="${bg}"/>` : '';
/* Create character paths and order them randomly */
let path =
[].concat(getTextPath(text, width, height, options))
.sort(() => Math.random() - 0.5);
let path = [].concat(getTextPath(text, width, height, options));
shuffleArray(path);
/* Join paths together to one */
path = mergePaths(path);
/* Randomize nodes and randomly split them */
@ -65,24 +74,27 @@ const createCaptcha = function (text, options) {
};
const create = function (options) {
options = Object.assign({}, opts, options);
const text = random.captchaText(options);
const data = createCaptcha(text, options);
return {text, data};
};
const createMathExpr = function (options) {
options = Object.assign({}, opts, options);
const expr = random.mathExpr(options.mathMin, options.mathMax, options.mathOperator);
const text = expr.text;
const data = createCaptcha(expr.equation, options);
return {text, data};
};
module.exports = createCaptcha;
module.exports.randomText = random.captchaText;
module.exports.create = create;
module.exports.createMathExpr = createMathExpr;
module.exports.createCaptcha = function (text, options) {
options = Object.assign({}, opts, options);
createCaptcha(text, options);
}
module.exports.options = opts;
module.exports.loadFont = optionMngr.loadFont;

View File

@ -5,8 +5,6 @@ const charPreset = require('./char-preset');
const fontPath = path.join(__dirname, '../fonts/Comismsh.ttf');
const font = opentype.loadSync(fontPath);
const ascender = font.ascender;
const descender = font.descender;
const options = {
width: 500,
@ -17,7 +15,8 @@ const options = {
size: 4,
ignoreChars: '',
fontSize: 220,
charPreset, font, ascender, descender,
charPreset,
font,
truncateLineProbability: 0.5,
truncateCurveProbability: 0.5,
truncateCurvePositionMin: 0.4,
@ -28,10 +27,7 @@ const options = {
};
const loadFont = filepath => {
const font = opentype.loadSync(filepath);
options.font = font;
options.ascender = font.ascender;
options.descender = font.descender;
return opentype.loadSync(filepath);
};
module.exports = {

View File

@ -1,5 +1,4 @@
'use strict';
const opts = require('./option-manager').options;
const randomInt = function (min, max) {
return Math.round(min + (Math.random() * (max - min)));
@ -19,17 +18,16 @@ exports.greyColor = function (min, max) {
return `#${int}${int}${int}`;
};
/*
* options must contain size, ignoreChars, charPreset
*/
exports.captchaText = function (options) {
if (typeof options === 'number') {
options = {size: options};
}
options = options || {};
const size = options.size;
const ignoreChars = options.ignoreChars;
let chars = options.charPreset;
const size = options.size || 4;
const ignoreChars = options.ignoreChars || '';
let i = -1;
let out = '';
let chars = options.charPreset || opts.charPreset;
if (ignoreChars) {
chars = stripCharsFromString(chars, ignoreChars);