@waiting/idcard-reader-base
Version:
449 lines (440 loc) • 14.3 kB
JavaScript
/**
* @waiting/idcard-reader-base
* 二代机具读卡
*
* @version 2.0.1
* @author waiting
* @license MIT
* @link https://github.com/waitingsong/node-idcard-reader-base#readme
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
var sharedCore = require('@waiting/shared-core');
var operators = require('rxjs/operators');
var rxrunscript = require('rxrunscript');
const config = {
appDir: '',
tmpDir: sharedCore.join(sharedCore.tmpdir(), 'idcard-reader'),
};
/** 二代证信息 */
const initialDataBase = {
name: '',
gender: 0,
genderName: '',
nation: '',
nationName: '',
birth: '',
address: '',
idc: '',
regorg: '',
startdate: '',
enddate: '',
};
const initialCompositeOpts = {
useComposite: false,
compositeDir: config.tmpDir,
compositeQuality: 35,
compositeType: 'jpg',
textColor: '#303030',
fontHwxhei: 'c:/Windows/Fonts/hwxhei.ttf',
fontOcrb: 'c:/Windows/Fonts/ocrb10bt.ttf',
fontSimhei: 'c:/Windows/Fonts/simhei.ttf',
};
/** 默认初始化参数 */
const initialDeviceOpts = {
debug: false,
dllTxt: '',
dllImage: '',
findCardRetryTimes: 1,
imgSaveDir: config.tmpDir,
port: 0,
searchAll: false,
};
/** 默认初始化参数 */
const initialOpts = {
...initialDeviceOpts,
...initialCompositeOpts,
};
const initialIDData = {
compositePath: '',
base: null,
imagePath: '',
};
/** 民族列表 */
const nationMap = new Map([
['01', '汉'],
['02', '蒙古'],
['03', '回'],
['04', '藏'],
['05', '维吾尔'],
['06', '苗'],
['07', '彝'],
['08', '壮'],
['09', '布依'],
['10', '朝鲜'],
['11', '满'],
['12', '侗'],
['13', '瑶'],
['14', '白'],
['15', '土家'],
['16', '哈尼'],
['17', '哈萨克'],
['18', '傣'],
['19', '黎'],
['20', '傈僳'],
['21', '佤'],
['22', '畲'],
['23', '高山'],
['24', '拉祜'],
['25', '水'],
['26', '东乡'],
['27', '纳西'],
['28', '景颇'],
['29', '柯尔克孜'],
['30', '土'],
['31', '达翰尔'],
['32', '仫佬'],
['33', '羌'],
['34', '布朗'],
['35', '撒拉'],
['36', '毛南'],
['37', '仡佬'],
['38', '锡伯'],
['39', '阿昌'],
['40', '普米'],
['41', '塔吉克'],
['42', '怒'],
['43', '乌孜别克'],
['44', '俄罗斯'],
['45', '鄂温克'],
['46', '德昂'],
['47', '保安'],
['48', '裕固'],
['49', '京'],
['50', '塔塔尔'],
['51', '独龙'],
['52', '鄂伦春'],
['53', '赫哲'],
['54', '门巴'],
['55', '珞巴'],
['56', '基诺'],
['57', '其它'],
['98', '外国人入籍'],
]);
/**
* Convert avatar bmp to png with transparent background
* Return avatar path
*/
function handleAvatar(path) {
// magick avatar.bmp -resize x353 -fuzz 9% -transparent '#FEFEFE' avatar.png
const target = sharedCore.normalize(sharedCore.join(config.tmpDir, 'avatar-' + Math.random().toString() + '.png')).replace(/\\/ug, '/');
const src = sharedCore.normalize(path).replace(/\\/ug, '/');
const cmd = `magick "${src}" -resize x353 -fuzz 9% -transparent "#FEFEFE" "${target}"`;
const ret = rxrunscript.run(cmd, null, { maxCmdLength: 4096 }).pipe(operators.mapTo(target));
return ret;
}
/**
* Fill base info text
*/
function handleBaseInfo(data, avatarPath, options) {
/* istanbul ignore else */
if (!data) {
throw new TypeError('base value invalid');
}
/* istanbul ignore else */
if (!options.fontHwxhei || !options.fontOcrb || !options.fontSimhei) {
throw new TypeError('font value invalid');
}
const assetsDir = sharedCore.join(config.appDir, 'assets');
const tpl = sharedCore.join(assetsDir, 'tpl.png');
const target = sharedCore.join(options.compositeDir, 'composite-' + Math.random().toString() + `.${options.compositeType}`)
.replace(/\\/ug, '/');
const txtColor = options.textColor;
const hwxhei = sharedCore.normalize(options.fontHwxhei).replace(/\\/ug, '/');
const orcb = sharedCore.normalize(options.fontOcrb).replace(/\\/ug, '/');
const simhei = sharedCore.normalize(options.fontSimhei).replace(/\\/ug, '/');
const ps = [
genParamName(data.name, txtColor, hwxhei),
genParamGender(data.genderName, txtColor, hwxhei),
genParamNation(data.nationName, txtColor, hwxhei),
genParamBirth(data.birth, txtColor, hwxhei),
genParamIdc(data.idc, txtColor, orcb),
genParamAddress(data.address, txtColor, hwxhei),
genParamRegOrg(data.regorg, txtColor, simhei),
genParamValidDate(data.startdate, data.enddate, txtColor, hwxhei),
];
const cmd = `magick ${tpl} ` + ps.join(' ')
+ ` -compose src-over "${avatarPath}" -geometry +619+125 -quality ${options.compositeQuality} -composite "${target}"`;
const ret = rxrunscript.run(cmd, null, { maxCmdLength: 4096 }).pipe(operators.mapTo(target), operators.tap(() => sharedCore.unlinkAsync(avatarPath)));
return ret;
}
function genParamName(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
return `-fill "${txtColor}" -font "${font}" -pointsize 42 -draw "text 208,146 '${val}'"`;
}
function genParamGender(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
return `-fill "${txtColor}" -font "${font}" -pointsize 34 -draw "text 208,220 '${val}'"`;
}
function genParamNation(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
return `-fill "${txtColor}" -font "${font}" -pointsize 34 -draw "text 395,220 '${val}'"`;
}
function genParamBirth(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
const year = val.slice(0, 4);
let month = val.slice(4, 6);
let day = val.slice(6, 8);
/* istanbul ignore else */
if (month.startsWith('0')) {
month = ' ' + month[1];
}
/* istanbul ignore else */
if (day.startsWith('0')) {
day = ' ' + day[1];
}
let ret = `-fill "${txtColor}" -font "${font}" -pointsize 33 -kerning 1 -draw "text 210,295 '${year}'"`;
ret += ` -fill "${txtColor}" -font "${font}" -pointsize 33 -draw "text 350,295 '${month}'"`;
ret += ` -fill "${txtColor}" -font "${font}" -pointsize 33 -draw "text 435,295 '${day}'"`;
return ret;
}
function genParamIdc(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
return `-fill "${txtColor}" -font "${font}" -pointsize 45 -kerning 1 -draw "text 355,561 '${val}'"`;
}
function genParamAddress(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
const len = val.length;
let ret = '';
let pos = 0;
let line = '';
let lineY = 368;
const lineHeight = 50;
do {
line = retrieveAddressLine(val, pos);
if (line.length) {
ret += ` -fill "${txtColor}" -font "${font}" -pointsize 33 -kerning 3 -draw "text 208,${lineY} '${line}'"`;
pos += line.length;
lineY += lineHeight;
}
else {
break;
}
} while (pos <= len);
return ret;
}
/* 11 Chinese every line */
function retrieveAddressLine(value, startPos) {
/* istanbul ignore else */
if (value.length <= startPos) {
return '';
}
const txt = value.slice(startPos);
/* istanbul ignore else */
if (txt.length > 10) {
if (/[\d\w-]/u.test(txt.slice(11, 12))) { // p11 is number or letter
const p12 = txt.slice(12, 13);
if (p12 && /[\d\w-]/u.test(p12)) {
return txt.slice(0, 10);
}
}
}
return txt.slice(0, 11);
}
function genParamRegOrg(value, txtColor, font) {
const val = value ? value.trim() : '';
/* istanbul ignore else */
if (!val) {
throw new TypeError('value invalid');
}
return `-fill "${txtColor}" -font "${font}" -pointsize 32 -kerning 3 -draw "text 413,1138 '${val}'"`;
}
function genParamValidDate(start, end, txtColor, font) {
const p1 = start
? start.slice(0, 4) + '.' + start.slice(4, 6) + '.' + start.slice(6, 8)
: '';
/* istanbul ignore else */
if (!p1) {
throw new TypeError('value invalid');
}
const p2 = Number.isNaN(+end)
? end
: end.slice(0, 4) + '.' + end.slice(4, 6) + '.' + end.slice(6, 8);
return `-fill "${txtColor}" -font "${font}" -pointsize 32 -kerning 1.6 -draw "text 413,1215 '${p1}-${p2}'"`;
}
function formatBaseData(data) {
const ret = { ...data };
if (ret.gender) {
switch (ret.gender) {
case 1:
ret.genderName = '男';
break;
case 2:
ret.genderName = '女';
break;
default:
ret.genderName = '未知';
break;
}
}
else if (ret.genderName && !ret.gender) {
switch (ret.genderName) {
case '男':
ret.gender = 1;
break;
case '女':
ret.gender = 2;
break;
default:
ret.gender = 0;
break;
}
}
if (ret.nation) {
const str = nationMap.get(ret.nation);
ret.nationName = str ? str.trim() : '未知';
ret.startdate = ret.startdate.trim();
ret.enddate = ret.enddate.trim();
}
else if (ret.nationName && !ret.nation) {
for (const [key, val] of nationMap) {
if (val === ret.nationName) {
ret.nation = key;
break;
}
}
}
return ret;
}
/* Composite image form base data. Return imagePath */
function composite(avatarPath, base, options) {
if (!base) {
throw new TypeError('base data value empty');
}
const ret$ = handleAvatar(avatarPath).pipe(operators.concatMap((avatarPNG) => {
return handleBaseInfo(base, avatarPNG, options);
}));
return ret$;
}
async function validateDllFile(path) {
if (!await sharedCore.isFileExists(path)) {
throw new Error('File not exists: ' + path);
}
}
async function testWrite(dir) {
if (!dir) {
throw new Error('value of imgSaveDir empty');
}
if (!await sharedCore.isDirExists(dir)) {
await sharedCore.createDirAsync(dir);
await sharedCore.createFileAsync(sharedCore.join(dir, '.test'), 'idctest'); // 创建测试文件
}
}
function parseDeviceOpts(options) {
const deviceOpts = { ...initialDeviceOpts };
if (options.dllTxt) {
deviceOpts.dllTxt = sharedCore.normalize(options.dllTxt);
}
else {
throw new Error('params dllTxt undefined or blank');
}
if (typeof options.dllImage === 'string' && options.dllImage) {
deviceOpts.dllImage = sharedCore.normalize(options.dllImage);
}
if (typeof options.imgSaveDir === 'string' && options.imgSaveDir) {
deviceOpts.imgSaveDir = sharedCore.normalize(options.imgSaveDir);
}
if (typeof options.debug === 'boolean') {
deviceOpts.debug = options.debug;
}
if (typeof options.searchAll === 'boolean') {
deviceOpts.searchAll = options.searchAll;
}
if (typeof options.findCardRetryTimes === 'number') {
deviceOpts.findCardRetryTimes = options.findCardRetryTimes;
}
if (Number.isNaN(deviceOpts.findCardRetryTimes) || deviceOpts.findCardRetryTimes < 0) {
deviceOpts.findCardRetryTimes = initialOpts.findCardRetryTimes;
}
if (typeof options.port === 'number' && options.port > 0) {
deviceOpts.port = options.port;
}
return deviceOpts;
}
function parseCompositeOpts(options) {
const compositeOpts = { ...initialCompositeOpts };
if (options.dllImage && options.useComposite) {
compositeOpts.useComposite = true;
}
if (options.compositeDir && typeof options.compositeDir === 'string') {
compositeOpts.compositeDir = sharedCore.normalize(options.compositeDir);
}
if (typeof options.compositeQuality === 'number'
&& options.compositeQuality >= 1 && options.compositeQuality <= 100) {
compositeOpts.compositeQuality = options.compositeQuality;
}
if (options.textColor) {
compositeOpts.textColor = options.textColor;
}
if (options.compositeType) {
if (!['bmp', 'gif', 'jpg', 'png', 'webp'].includes(options.compositeType)) {
throw new TypeError('compositeType value invalid');
}
compositeOpts.compositeType = options.compositeType;
}
else {
compositeOpts.compositeType = initialOpts.compositeType;
}
if (options.fontHwxhei) {
compositeOpts.fontHwxhei = options.fontHwxhei;
}
if (options.fontOcrb) {
compositeOpts.fontOcrb = options.fontOcrb;
}
if (options.fontSimhei) {
compositeOpts.fontSimhei = options.fontSimhei;
}
return compositeOpts;
}
// base directory of this module
config.appDir = sharedCore.join(__dirname, '/..');
exports.composite = composite;
exports.config = config;
exports.formatBaseData = formatBaseData;
exports.handleAvatar = handleAvatar;
exports.handleBaseInfo = handleBaseInfo;
exports.initialCompositeOpts = initialCompositeOpts;
exports.initialDataBase = initialDataBase;
exports.initialDeviceOpts = initialDeviceOpts;
exports.initialIDData = initialIDData;
exports.initialOpts = initialOpts;
exports.nationMap = nationMap;
exports.parseCompositeOpts = parseCompositeOpts;
exports.parseDeviceOpts = parseDeviceOpts;
exports.testWrite = testWrite;
exports.validateDllFile = validateDllFile;
//# sourceMappingURL=index.cjs.js.map