[UPDATED] refactor so that user can load a new font

This commit is contained in:
Weilin Shi 2017-01-04 21:11:47 +08:00
parent 69bdacfcce
commit 594e118153
6 changed files with 82 additions and 89 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = tab
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
[{package.json,*.yml}]
indent_style = space
indent_size = 2

View File

@ -1,36 +1,19 @@
'use strict';
const assert = require('assert');
const path = require('path');
const opentype = require('opentype.js');
const fontPath = path.join(__dirname, '../fonts/Comismsh.ttf');
const font = opentype.loadSync(fontPath);
const ascender = font.ascender;
const descender = font.descender;
const defaultOption = {
fontSize: 72,
x: 0,
y: 0,
noise: 1
};
module.exports = function (text, options) {
options = Object.assign({}, defaultOption, options);
const ch = text.trim()[0];
module.exports = function (text, opts) {
const ch = text[0];
assert(ch, 'expect a string');
const fontSize = options.fontSize;
const fontScale = 1 / font.unitsPerEm * fontSize;
const fontSize = opts.fontSize;
const fontScale = 1 / opts.font.unitsPerEm * fontSize;
const glyph = font.charToGlyph(ch);
const glyph = opts.font.charToGlyph(ch);
const width = glyph.advanceWidth ? glyph.advanceWidth * fontScale : 0;
const left = options.x - (width / 2);
const left = opts.x - (width / 2);
const height = (ascender + descender) * fontScale;
const top = options.y + (height / 2);
const height = (opts.ascender + opts.descender) * fontScale;
const top = opts.y + (height / 2);
const path = glyph.getPath(left, top, fontSize);
const pathData = path.toPathData();

View File

@ -1,23 +1,25 @@
'use strict';
const chToPath = require('./ch-to-path');
const random = require('./random');
const optionMngr = require('./option-manager');
const opts = optionMngr.options;
const getLineNoise = function (width, height, options) {
const hasColor = options.color;
const noiseString = [];
const noiseLines = [];
let i = -1;
while (++i < options.noise) {
const start = `${random.int(5, 25)} ${random.int(10, height - 10)}`;
const end = `${random.int(width - 25, width - 5)} ${random.int(10, height - 10)}`;
const mid1 = `${random.int((width / 2) - 25, (width / 2) + 25)} ${random.int(10, height - 10)}`;
const mid2 = `${random.int((width / 2) - 25, (width / 2) + 25)} ${random.int(10, height - 10)}`;
const start = `${random.int(1, 21)} ${random.int(1, height - 1)}`;
const end = `${random.int(width - 21, width - 1)} ${random.int(1, height - 1)}`;
const mid1 = `${random.int((width / 2) - 21, (width / 2) + 21)} ${random.int(1, height - 1)}`;
const mid2 = `${random.int((width / 2) - 21, (width / 2) + 21)} ${random.int(1, height - 1)}`;
const color = hasColor ? random.color() : random.greyColor();
noiseString.push(`<path d="M${start} C${mid1},${mid2},${end}"
stroke="${color}" fill="none"/>`);
noiseLines.push(`<path d="M${start} C${mid1},${mid2},${end}" stroke="${color}" fill="none"/>`);
}
return noiseString.join('');
return noiseLines;
};
const getText = function (text, width, height, options) {
@ -29,30 +31,19 @@ const getText = function (text, width, height, options) {
while (++i < len) {
const x = spacing * (i + 1);
const y = height / 2;
const fontSize = height;
const charPath = chToPath(
text[i],
Object.assign({x, y, fontSize}, options)
);
const charPath = chToPath(text[i], Object.assign({x, y}, options));
const color = options.color ?
random.color(options.background) : random.greyColor(0, 4);
out.push(`<path fill="${color}" d="${charPath}"/>`);
}
return out.join('');
return out;
};
const defaultOption = {
width: 150,
height: 50,
noise: 1,
color: false,
background: ''
};
const createCaptcha = function (text, options) {
text = text || random.captchaText();
options = Object.assign({}, defaultOption, options);
options = Object.assign({}, opts, options);
const width = options.width;
const height = options.height;
const bg = options.background;
@ -62,14 +53,15 @@ const createCaptcha = function (text, options) {
const bgRect = bg ?
`<rect width="100%" height="100%" fill="${bg}"/>` : '';
const lineNoise = getLineNoise(width, height, options);
const textPath = getText(text, width, height, options);
const xml = `<svg xmlns="http://www.w3.org/2000/svg"
width="${width}" height="${height}">
${bgRect} ${textPath} ${lineNoise}
</svg>`;
const paths =
[].concat(getLineNoise(width, height, options))
.concat(getText(text, width, height, options))
.sort(() => Math.random() - 0.5)
.join('');
const start = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">`;
const xml = `${start}${bgRect}${paths}</svg>`;
return xml.replace(/[\t]/g, '').replace(/\n(\W)/g, '$1');
return xml;
};
const create = function (options) {
@ -91,3 +83,5 @@ module.exports = createCaptcha;
module.exports.randomText = random.captchaText;
module.exports.create = create;
module.exports.createMathExpr = createMathExpr;
module.exports.options = opts;
module.exports.loadFont = optionMngr.loadFont;

32
lib/option-manager.js Normal file
View File

@ -0,0 +1,32 @@
'use strict';
const path = require('path');
const opentype = require('opentype.js');
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: 150,
height: 50,
noise: 1,
color: false,
background: '',
size: 4,
ignoreChars: '',
fontSize: 56,
charPreset, font, ascender, descender
};
const loadFont = filepath => {
const font = opentype.loadSync(filepath);
options.font = font;
options.ascender = font.ascender;
options.descender = font.descender;
};
module.exports = {
options, loadFont
};

View File

@ -1,6 +1,7 @@
'use strict';
const opts = require('./option-manager').options;
const presets = require('./char-preset');
const presets = opts.charPreset;
const randomInt = function (min, max) {
return Math.round(min + (Math.random() * (max - min)));
@ -45,44 +46,15 @@ exports.captchaText = function (options) {
return out;
};
const ops = ['+', '-', '*', '/'];
/**
* returns an object that has the following props:
* text, equation
*/
exports.mathExpr = function () {
const op = ops[0];
let left;
let right;
let text;
switch (op) {
case '+':
left = randomInt(1, 9);
right = randomInt(1, 9);
text = (left + right).toString();
break;
case '-':
right = randomInt(1, 9);
left = right + randomInt(1, 8);
text = (left - right).toString();
break;
case '*':
left = randomInt(1, 8);
right = randomInt(2, 9);
text = (left * right).toString();
break;
default: // division
right = randomInt(2, 8);
left = right * randomInt(2, 9);
text = Math.round(left / right).toString();
break;
}
const equation = left + op + right;
const left = randomInt(1, 9);
const right = randomInt(1, 9);
const text = (left + right).toString();
const equation = left + '+' + right;
return {text, equation};
};

View File

@ -75,7 +75,7 @@ describe('random function', () => {
assert(/^\d+$/.test(expr.text));
assert(typeof expr.equation === 'string');
assert(/^\d+[\+\-\*\/]\d+$/.test(expr.equation));
assert(/^\d+[+\-*/]\d+$/.test(expr.equation));
}
});
});