id-scanner-lib 1.3.3 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -410
- package/dist/id-scanner-lib.esm.js +4826 -0
- package/dist/id-scanner-lib.esm.js.map +1 -0
- package/dist/id-scanner-lib.js +4858 -0
- package/dist/id-scanner-lib.js.map +1 -0
- package/dist/types/browser-image-compression.d.ts +19 -0
- package/dist/types/tesseract.d.ts +280 -0
- package/package.json +89 -78
- package/src/core/base-module.ts +78 -0
- package/src/core/camera-manager.ts +813 -0
- package/src/core/config.ts +305 -0
- package/src/core/errors.ts +174 -0
- package/src/core/event-emitter.test.ts +42 -0
- package/src/core/event-emitter.ts +110 -0
- package/src/core/loading-state.test.ts +67 -0
- package/src/core/loading-state.ts +156 -0
- package/src/core/logger.test.ts +49 -0
- package/src/core/logger.ts +549 -0
- package/src/core/module-manager.ts +163 -0
- package/src/core/plugin-manager.ts +429 -0
- package/src/core/resource-manager.ts +762 -0
- package/src/core/result.ts +163 -0
- package/src/core/scanner-factory.ts +236 -0
- package/src/index.ts +117 -939
- package/src/interfaces/external-types.ts +200 -0
- package/src/interfaces/face-detection.ts +309 -0
- package/src/interfaces/scanner-module.ts +384 -0
- package/src/modules/face/face-detector.ts +988 -0
- package/src/modules/face/index.ts +208 -0
- package/src/modules/face/liveness-detector.ts +908 -0
- package/src/modules/face/types.ts +133 -0
- package/src/{id-recognition → modules/id-card}/anti-fake-detector.ts +274 -240
- package/src/modules/id-card/id-card-detector.ts +474 -0
- package/src/modules/id-card/index.ts +425 -0
- package/src/{id-recognition → modules/id-card}/ocr-processor.ts +149 -92
- package/src/modules/id-card/ocr-worker.ts +259 -0
- package/src/modules/id-card/types.ts +178 -0
- package/src/modules/qrcode/index.ts +175 -0
- package/src/modules/qrcode/qr-code-scanner.ts +231 -0
- package/src/modules/qrcode/types.ts +169 -0
- package/src/types/common.test.ts +99 -0
- package/src/types/common.ts +166 -0
- package/src/types/tesseract.d.ts +265 -22
- package/src/utils/camera.test.ts +30 -0
- package/src/utils/camera.ts +4 -1
- package/src/utils/error-handler.test.ts +137 -0
- package/src/utils/error-handler.ts +110 -0
- package/src/utils/image-processing.ts +68 -49
- package/src/utils/index.test.ts +186 -0
- package/src/utils/index.ts +429 -0
- package/src/utils/performance.ts +168 -131
- package/src/utils/resource-manager.ts +65 -146
- package/src/utils/retry.test.ts +142 -0
- package/src/utils/retry.ts +282 -0
- package/src/utils/types.ts +90 -2
- package/src/utils/utils.test.ts +171 -0
- package/src/utils/worker.ts +123 -84
- package/src/version.ts +11 -0
- package/tools/scaffold.js +543 -0
- package/dist/id-scanner-core.esm.js +0 -11349
- package/dist/id-scanner-core.js +0 -11361
- package/dist/id-scanner-core.min.js +0 -1
- package/dist/id-scanner-ocr.esm.js +0 -2319
- package/dist/id-scanner-ocr.js +0 -2328
- package/dist/id-scanner-ocr.min.js +0 -1
- package/dist/id-scanner-qr.esm.js +0 -1296
- package/dist/id-scanner-qr.js +0 -1305
- package/dist/id-scanner-qr.min.js +0 -1
- package/dist/id-scanner.js +0 -4561
- package/dist/id-scanner.min.js +0 -1
- package/src/core.ts +0 -138
- package/src/demo/demo.ts +0 -204
- package/src/id-recognition/data-extractor.ts +0 -262
- package/src/id-recognition/id-detector.ts +0 -510
- package/src/id-recognition/ocr-worker.ts +0 -156
- package/src/index-umd.ts +0 -477
- package/src/ocr-module.ts +0 -187
- package/src/qr-module.ts +0 -179
- package/src/scanner/barcode-scanner.ts +0 -251
- package/src/scanner/qr-scanner.ts +0 -167
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import imageCompression from "browser-image-compression"
|
|
9
|
+
import { Point, Rect, ImageProcessingOptions } from './types';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* 图像处理器配置选项
|
|
@@ -481,68 +482,86 @@ export class ImageProcessor {
|
|
|
481
482
|
}
|
|
482
483
|
|
|
483
484
|
/**
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* @param imageData 原始图像数据
|
|
485
|
+
* 将图像调整到指定大小
|
|
486
|
+
* @param image 输入图像
|
|
487
487
|
* @param maxWidth 最大宽度
|
|
488
488
|
* @param maxHeight 最大高度
|
|
489
|
-
* @param
|
|
490
|
-
* @returns
|
|
489
|
+
* @param keepAspectRatio 是否保持宽高比
|
|
490
|
+
* @returns 调整后的图像
|
|
491
491
|
*/
|
|
492
|
-
static resizeImage(
|
|
493
|
-
|
|
492
|
+
public static resizeImage(
|
|
493
|
+
image: ImageData | HTMLImageElement | HTMLCanvasElement,
|
|
494
494
|
maxWidth: number,
|
|
495
495
|
maxHeight: number,
|
|
496
|
-
|
|
496
|
+
keepAspectRatio: boolean = true
|
|
497
497
|
): ImageData {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
498
|
+
// 创建canvas元素
|
|
499
|
+
const canvas = document.createElement('canvas');
|
|
500
|
+
const ctx = canvas.getContext('2d');
|
|
501
|
+
|
|
502
|
+
if (!ctx) {
|
|
503
|
+
throw new Error('无法创建Canvas上下文');
|
|
503
504
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
let
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if (
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
505
|
+
|
|
506
|
+
// 获取图像尺寸
|
|
507
|
+
let width: number;
|
|
508
|
+
let height: number;
|
|
509
|
+
|
|
510
|
+
if (image instanceof ImageData) {
|
|
511
|
+
width = image.width;
|
|
512
|
+
height = image.height;
|
|
513
|
+
} else {
|
|
514
|
+
width = image.width;
|
|
515
|
+
height = image.height;
|
|
513
516
|
}
|
|
514
|
-
|
|
515
|
-
//
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
517
|
+
|
|
518
|
+
// 计算调整后的尺寸
|
|
519
|
+
let newWidth = width;
|
|
520
|
+
let newHeight = height;
|
|
521
|
+
|
|
522
|
+
if (keepAspectRatio) {
|
|
523
|
+
if (width > height) {
|
|
524
|
+
if (width > maxWidth) {
|
|
525
|
+
newHeight = Math.round(height * (maxWidth / width));
|
|
526
|
+
newWidth = maxWidth;
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
if (height > maxHeight) {
|
|
530
|
+
newWidth = Math.round(width * (maxHeight / height));
|
|
531
|
+
newHeight = maxHeight;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
newWidth = Math.min(width, maxWidth);
|
|
536
|
+
newHeight = Math.min(height, maxHeight);
|
|
523
537
|
}
|
|
524
|
-
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
538
|
+
|
|
539
|
+
// 设置canvas尺寸
|
|
540
|
+
canvas.width = newWidth;
|
|
541
|
+
canvas.height = newHeight;
|
|
542
|
+
|
|
543
|
+
// 绘制调整后的图像
|
|
544
|
+
if (image instanceof ImageData) {
|
|
545
|
+
// 创建临时canvas存储ImageData
|
|
546
|
+
const tempCanvas = document.createElement('canvas');
|
|
547
|
+
const tempCtx = tempCanvas.getContext('2d');
|
|
548
|
+
|
|
531
549
|
if (!tempCtx) {
|
|
532
|
-
|
|
550
|
+
throw new Error('无法创建临时Canvas上下文');
|
|
533
551
|
}
|
|
534
552
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
553
|
+
tempCanvas.width = image.width;
|
|
554
|
+
tempCanvas.height = image.height;
|
|
555
|
+
tempCtx.putImageData(image, 0, 0);
|
|
556
|
+
|
|
557
|
+
// 绘制调整后的图像
|
|
558
|
+
ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, newWidth, newHeight);
|
|
559
|
+
} else {
|
|
560
|
+
ctx.drawImage(image, 0, 0, width, height, 0, 0, newWidth, newHeight);
|
|
561
|
+
}
|
|
543
562
|
|
|
544
|
-
//
|
|
545
|
-
return ctx.getImageData(0, 0, newWidth, newHeight)
|
|
563
|
+
// 返回调整后的ImageData
|
|
564
|
+
return ctx.getImageData(0, 0, newWidth, newHeight);
|
|
546
565
|
}
|
|
547
566
|
|
|
548
567
|
/**
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 通用工具测试
|
|
3
|
+
* @description 测试通用工具函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
delay,
|
|
8
|
+
throttle,
|
|
9
|
+
debounce,
|
|
10
|
+
formatBytes,
|
|
11
|
+
generateUUID,
|
|
12
|
+
chunk,
|
|
13
|
+
safeParseJSON,
|
|
14
|
+
clamp,
|
|
15
|
+
isValidUrl,
|
|
16
|
+
browserCapabilities
|
|
17
|
+
} from './index';
|
|
18
|
+
|
|
19
|
+
describe('delay', () => {
|
|
20
|
+
it('should delay for specified milliseconds', async () => {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
await delay(50);
|
|
23
|
+
const elapsed = Date.now() - start;
|
|
24
|
+
|
|
25
|
+
expect(elapsed).toBeGreaterThanOrEqual(45);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('throttle', () => {
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.useFakeTimers();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
jest.useRealTimers();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should throttle function calls', () => {
|
|
39
|
+
let count = 0;
|
|
40
|
+
const fn = throttle(() => count++, 100);
|
|
41
|
+
|
|
42
|
+
fn();
|
|
43
|
+
fn();
|
|
44
|
+
fn();
|
|
45
|
+
|
|
46
|
+
expect(count).toBe(1);
|
|
47
|
+
|
|
48
|
+
jest.advanceTimersByTime(100);
|
|
49
|
+
|
|
50
|
+
fn();
|
|
51
|
+
expect(count).toBe(2);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('debounce', () => {
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
jest.useFakeTimers();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
jest.useRealTimers();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should debounce function calls', () => {
|
|
65
|
+
let count = 0;
|
|
66
|
+
const fn = debounce(() => count++, 100);
|
|
67
|
+
|
|
68
|
+
fn();
|
|
69
|
+
fn();
|
|
70
|
+
fn();
|
|
71
|
+
|
|
72
|
+
expect(count).toBe(0);
|
|
73
|
+
|
|
74
|
+
jest.advanceTimersByTime(100);
|
|
75
|
+
|
|
76
|
+
expect(count).toBe(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should execute immediately when immediate is true', () => {
|
|
80
|
+
let count = 0;
|
|
81
|
+
const fn = debounce(() => count++, 100, true);
|
|
82
|
+
|
|
83
|
+
fn();
|
|
84
|
+
// Immediate should execute once
|
|
85
|
+
expect(count).toBe(1);
|
|
86
|
+
|
|
87
|
+
// Advance time and check if it runs again
|
|
88
|
+
jest.advanceTimersByTime(100);
|
|
89
|
+
// Depending on implementation, this may vary
|
|
90
|
+
expect(count).toBeGreaterThanOrEqual(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('formatBytes', () => {
|
|
95
|
+
it('should format bytes correctly', () => {
|
|
96
|
+
expect(formatBytes(0)).toBe('0 Bytes');
|
|
97
|
+
expect(formatBytes(1024)).toBe('1 KB');
|
|
98
|
+
expect(formatBytes(1024 * 1024)).toBe('1 MB');
|
|
99
|
+
expect(formatBytes(1024 * 1024 * 1024)).toBe('1 GB');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle decimals', () => {
|
|
103
|
+
expect(formatBytes(1536, 2)).toBe('1.5 KB');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('generateUUID', () => {
|
|
108
|
+
it('should generate valid UUID', () => {
|
|
109
|
+
const uuid = generateUUID();
|
|
110
|
+
|
|
111
|
+
expect(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should generate unique UUIDs', () => {
|
|
115
|
+
const uuids = new Set<string>();
|
|
116
|
+
for (let i = 0; i < 100; i++) {
|
|
117
|
+
uuids.add(generateUUID());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
expect(uuids.size).toBe(100);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('chunk', () => {
|
|
125
|
+
it('should split array into chunks', () => {
|
|
126
|
+
const arr = [1, 2, 3, 4, 5, 6, 7];
|
|
127
|
+
const chunks = chunk(arr, 3);
|
|
128
|
+
|
|
129
|
+
expect(chunks).toEqual([[1, 2, 3], [4, 5, 6], [7]]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should throw error for invalid chunk size', () => {
|
|
133
|
+
expect(() => chunk([1, 2, 3], 0)).toThrow();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('safeParseJSON', () => {
|
|
138
|
+
it('should parse valid JSON', () => {
|
|
139
|
+
const result = safeParseJSON('{"a": 1}', { default: true });
|
|
140
|
+
expect(result).toEqual({ a: 1 });
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should return fallback for invalid JSON', () => {
|
|
144
|
+
const result = safeParseJSON<boolean>('invalid', true);
|
|
145
|
+
expect(result).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('clamp', () => {
|
|
150
|
+
it('should clamp value within range', () => {
|
|
151
|
+
expect(clamp(5, 0, 10)).toBe(5);
|
|
152
|
+
expect(clamp(-5, 0, 10)).toBe(0);
|
|
153
|
+
expect(clamp(15, 0, 10)).toBe(10);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('isValidUrl', () => {
|
|
158
|
+
it('should validate URLs', () => {
|
|
159
|
+
expect(isValidUrl('https://example.com')).toBe(true);
|
|
160
|
+
expect(isValidUrl('http://test.com/path')).toBe(true);
|
|
161
|
+
expect(isValidUrl('invalid')).toBe(false);
|
|
162
|
+
expect(isValidUrl('not a url')).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('browserCapabilities', () => {
|
|
167
|
+
it('should check camera support', () => {
|
|
168
|
+
expect(typeof browserCapabilities.hasCamera).toBe('function');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should check WebAssembly support', () => {
|
|
172
|
+
expect(typeof browserCapabilities.hasWasm).toBe('function');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should check WebWorker support', () => {
|
|
176
|
+
expect(typeof browserCapabilities.hasWebWorker).toBe('function');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should check WebGL support', () => {
|
|
180
|
+
expect(typeof browserCapabilities.hasWebGL).toBe('function');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should use supports method', () => {
|
|
184
|
+
expect(typeof browserCapabilities.supports).toBe('function');
|
|
185
|
+
});
|
|
186
|
+
});
|