@pallasoft/ps-file-components 1.3.0 → 1.5.0

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.
Files changed (216) hide show
  1. package/dist/components/ps-document-scanner-loader.d.ts +1 -0
  2. package/dist/components/ps-document-scanner-loader.js +40 -0
  3. package/dist/components/ps-document-scanner-sheet.d.ts +2 -0
  4. package/dist/components/ps-document-scanner-sheet.js +186 -0
  5. package/dist/components/ps-document-scanner.d.ts +2 -0
  6. package/dist/components/ps-document-scanner.js +17 -0
  7. package/dist/components/ps-file-picker-actions.d.ts +10 -0
  8. package/dist/components/ps-file-picker-actions.js +21 -0
  9. package/dist/components/ps-file-picker.d.ts +7 -1
  10. package/dist/components/ps-file-picker.js +30 -6
  11. package/dist/components/ps-file-viewer-pane.d.ts +2 -0
  12. package/dist/components/ps-file-viewer-pane.js +39 -0
  13. package/dist/components/ps-scan-button.d.ts +13 -0
  14. package/dist/components/ps-scan-button.js +9 -0
  15. package/dist/components/ps-scan-camera-view.d.ts +27 -0
  16. package/dist/components/ps-scan-camera-view.js +34 -0
  17. package/dist/components/ps-scan-crop-editor.d.ts +13 -0
  18. package/dist/components/ps-scan-crop-editor.js +69 -0
  19. package/dist/components/ps-scan-denied-steps.d.ts +6 -0
  20. package/dist/components/ps-scan-denied-steps.js +33 -0
  21. package/dist/components/ps-scan-icon-button.d.ts +11 -0
  22. package/dist/components/ps-scan-icon-button.js +9 -0
  23. package/dist/components/ps-scan-illustration.d.ts +1 -0
  24. package/dist/components/ps-scan-illustration.js +6 -0
  25. package/dist/components/ps-scan-loupe.d.ts +10 -0
  26. package/dist/components/ps-scan-loupe.js +31 -0
  27. package/dist/components/ps-scan-page-list.d.ts +14 -0
  28. package/dist/components/ps-scan-page-list.js +22 -0
  29. package/dist/components/ps-scan-page-stack.d.ts +7 -0
  30. package/dist/components/ps-scan-page-stack.js +6 -0
  31. package/dist/components/ps-scan-permission-view.d.ts +11 -0
  32. package/dist/components/ps-scan-permission-view.js +48 -0
  33. package/dist/components/ps-scan-photo-button.d.ts +8 -0
  34. package/dist/components/ps-scan-photo-button.js +20 -0
  35. package/dist/components/ps-scan-review.d.ts +24 -0
  36. package/dist/components/ps-scan-review.js +37 -0
  37. package/dist/components/ps-scan-shutter.d.ts +7 -0
  38. package/dist/components/ps-scan-shutter.js +6 -0
  39. package/dist/components/ps-scan-viewfinder-overlay.d.ts +8 -0
  40. package/dist/components/ps-scan-viewfinder-overlay.js +28 -0
  41. package/dist/components/ps-upload-row.js +1 -1
  42. package/dist/constants/ps-document-scanner-labels.d.ts +80 -0
  43. package/dist/constants/ps-document-scanner-labels.js +83 -0
  44. package/dist/constants/ps-file-picker-labels.d.ts +5 -3
  45. package/dist/constants/ps-file-picker-labels.js +5 -3
  46. package/dist/constants/ps-scan-camera-status.d.ts +11 -0
  47. package/dist/constants/ps-scan-camera-status.js +13 -0
  48. package/dist/constants/ps-scan-chrome-classes.d.ts +12 -0
  49. package/dist/constants/ps-scan-chrome-classes.js +15 -0
  50. package/dist/constants/ps-scan-config.d.ts +48 -0
  51. package/dist/constants/ps-scan-config.js +51 -0
  52. package/dist/constants/ps-scan-corner-order.d.ts +1 -0
  53. package/dist/constants/ps-scan-corner-order.js +4 -0
  54. package/dist/constants/ps-scan-filter.d.ts +7 -0
  55. package/dist/constants/ps-scan-filter.js +9 -0
  56. package/dist/constants/ps-scan-output-format.d.ts +5 -0
  57. package/dist/constants/ps-scan-output-format.js +7 -0
  58. package/dist/constants/ps-scan-platform.d.ts +6 -0
  59. package/dist/constants/ps-scan-platform.js +8 -0
  60. package/dist/constants/ps-scan-step.d.ts +6 -0
  61. package/dist/constants/ps-scan-step.js +8 -0
  62. package/dist/esm/components/ps-document-scanner-loader.js +4 -0
  63. package/dist/esm/components/ps-document-scanner-sheet.js +182 -0
  64. package/dist/esm/components/ps-document-scanner.js +13 -0
  65. package/dist/esm/components/ps-file-picker-actions.js +17 -0
  66. package/dist/esm/components/ps-file-picker.js +31 -7
  67. package/dist/esm/components/ps-file-viewer-pane.js +35 -0
  68. package/dist/esm/components/ps-scan-button.js +5 -0
  69. package/dist/esm/components/ps-scan-camera-view.js +30 -0
  70. package/dist/esm/components/ps-scan-crop-editor.js +65 -0
  71. package/dist/esm/components/ps-scan-denied-steps.js +29 -0
  72. package/dist/esm/components/ps-scan-icon-button.js +5 -0
  73. package/dist/esm/components/ps-scan-illustration.js +2 -0
  74. package/dist/esm/components/ps-scan-loupe.js +27 -0
  75. package/dist/esm/components/ps-scan-page-list.js +18 -0
  76. package/dist/esm/components/ps-scan-page-stack.js +2 -0
  77. package/dist/esm/components/ps-scan-permission-view.js +44 -0
  78. package/dist/esm/components/ps-scan-photo-button.js +16 -0
  79. package/dist/esm/components/ps-scan-review.js +33 -0
  80. package/dist/esm/components/ps-scan-shutter.js +2 -0
  81. package/dist/esm/components/ps-scan-viewfinder-overlay.js +24 -0
  82. package/dist/esm/components/ps-upload-row.js +1 -1
  83. package/dist/esm/constants/ps-document-scanner-labels.js +80 -0
  84. package/dist/esm/constants/ps-file-picker-labels.js +5 -3
  85. package/dist/esm/constants/ps-scan-camera-status.js +10 -0
  86. package/dist/esm/constants/ps-scan-chrome-classes.js +12 -0
  87. package/dist/esm/constants/ps-scan-config.js +48 -0
  88. package/dist/esm/constants/ps-scan-corner-order.js +1 -0
  89. package/dist/esm/constants/ps-scan-filter.js +6 -0
  90. package/dist/esm/constants/ps-scan-output-format.js +4 -0
  91. package/dist/esm/constants/ps-scan-platform.js +5 -0
  92. package/dist/esm/constants/ps-scan-step.js +5 -0
  93. package/dist/esm/hooks/use-ps-focus-trap.js +45 -0
  94. package/dist/esm/hooks/use-ps-scan-camera.js +99 -0
  95. package/dist/esm/hooks/use-ps-scan-live-detection.js +45 -0
  96. package/dist/esm/hooks/use-ps-scan-pages.js +62 -0
  97. package/dist/esm/hooks/use-ps-scan-previews.js +57 -0
  98. package/dist/esm/index.js +8 -0
  99. package/dist/esm/models/ps-document-scanner-props.js +1 -0
  100. package/dist/esm/models/ps-document-scanner-sheet-props.js +1 -0
  101. package/dist/esm/models/ps-file-picker-scan.js +1 -0
  102. package/dist/esm/models/ps-file-viewer-pane-props.js +1 -0
  103. package/dist/esm/models/ps-scan-added-page.js +1 -0
  104. package/dist/esm/models/ps-scan-camera-session.js +1 -0
  105. package/dist/esm/models/ps-scan-decoded-image.js +1 -0
  106. package/dist/esm/models/ps-scan-levels.js +1 -0
  107. package/dist/esm/models/ps-scan-live-detection.js +1 -0
  108. package/dist/esm/models/ps-scan-output-options.js +1 -0
  109. package/dist/esm/models/ps-scan-page.js +1 -0
  110. package/dist/esm/models/ps-scan-pdf-page.js +1 -0
  111. package/dist/esm/models/ps-scan-prepared.js +1 -0
  112. package/dist/esm/models/ps-scan-quad.js +1 -0
  113. package/dist/esm/models/ps-scan-raster.js +1 -0
  114. package/dist/esm/models/ps-scan-rendered-page.js +1 -0
  115. package/dist/esm/models/ps-scan-stability.js +1 -0
  116. package/dist/esm/services/ps-scan-camera-service.js +95 -0
  117. package/dist/esm/services/ps-scan-canvas-service.js +52 -0
  118. package/dist/esm/services/ps-scan-edge-service.js +156 -0
  119. package/dist/esm/services/ps-scan-enhance-service.js +97 -0
  120. package/dist/esm/services/ps-scan-file-name-service.js +25 -0
  121. package/dist/esm/services/ps-scan-geometry-service.js +116 -0
  122. package/dist/esm/services/ps-scan-homography-service.js +46 -0
  123. package/dist/esm/services/ps-scan-hull-service.js +59 -0
  124. package/dist/esm/services/ps-scan-image-filter-service.js +52 -0
  125. package/dist/esm/services/ps-scan-order-service.js +13 -0
  126. package/dist/esm/services/ps-scan-output-service.js +24 -0
  127. package/dist/esm/services/ps-scan-pdf-service.js +56 -0
  128. package/dist/esm/services/ps-scan-pipeline-service.js +40 -0
  129. package/dist/esm/services/ps-scan-platform-service.js +15 -0
  130. package/dist/esm/services/ps-scan-raster-service.js +67 -0
  131. package/dist/esm/services/ps-scan-stability-service.js +41 -0
  132. package/dist/esm/services/ps-scan-template-service.js +5 -0
  133. package/dist/esm/services/ps-scan-warp-service.js +35 -0
  134. package/dist/hooks/use-ps-focus-trap.d.ts +2 -0
  135. package/dist/hooks/use-ps-focus-trap.js +49 -0
  136. package/dist/hooks/use-ps-scan-camera.d.ts +11 -0
  137. package/dist/hooks/use-ps-scan-camera.js +103 -0
  138. package/dist/hooks/use-ps-scan-live-detection.d.ts +3 -0
  139. package/dist/hooks/use-ps-scan-live-detection.js +49 -0
  140. package/dist/hooks/use-ps-scan-pages.d.ts +12 -0
  141. package/dist/hooks/use-ps-scan-pages.js +66 -0
  142. package/dist/hooks/use-ps-scan-previews.d.ts +2 -0
  143. package/dist/hooks/use-ps-scan-previews.js +61 -0
  144. package/dist/index.d.ts +8 -0
  145. package/dist/index.js +8 -0
  146. package/dist/models/ps-document-scanner-props.d.ts +16 -0
  147. package/dist/models/ps-document-scanner-props.js +2 -0
  148. package/dist/models/ps-document-scanner-sheet-props.d.ts +14 -0
  149. package/dist/models/ps-document-scanner-sheet-props.js +2 -0
  150. package/dist/models/ps-file-picker-scan.d.ts +4 -0
  151. package/dist/models/ps-file-picker-scan.js +2 -0
  152. package/dist/models/ps-file-viewer-pane-props.d.ts +12 -0
  153. package/dist/models/ps-file-viewer-pane-props.js +2 -0
  154. package/dist/models/ps-scan-added-page.d.ts +5 -0
  155. package/dist/models/ps-scan-added-page.js +2 -0
  156. package/dist/models/ps-scan-camera-session.d.ts +5 -0
  157. package/dist/models/ps-scan-camera-session.js +2 -0
  158. package/dist/models/ps-scan-decoded-image.d.ts +6 -0
  159. package/dist/models/ps-scan-decoded-image.js +2 -0
  160. package/dist/models/ps-scan-levels.d.ts +4 -0
  161. package/dist/models/ps-scan-levels.js +2 -0
  162. package/dist/models/ps-scan-live-detection.d.ts +5 -0
  163. package/dist/models/ps-scan-live-detection.js +2 -0
  164. package/dist/models/ps-scan-output-options.d.ts +7 -0
  165. package/dist/models/ps-scan-output-options.js +2 -0
  166. package/dist/models/ps-scan-page.d.ts +12 -0
  167. package/dist/models/ps-scan-page.js +2 -0
  168. package/dist/models/ps-scan-pdf-page.d.ts +5 -0
  169. package/dist/models/ps-scan-pdf-page.js +2 -0
  170. package/dist/models/ps-scan-prepared.d.ts +7 -0
  171. package/dist/models/ps-scan-prepared.js +2 -0
  172. package/dist/models/ps-scan-quad.d.ts +2 -0
  173. package/dist/models/ps-scan-quad.js +2 -0
  174. package/dist/models/ps-scan-raster.d.ts +5 -0
  175. package/dist/models/ps-scan-raster.js +2 -0
  176. package/dist/models/ps-scan-rendered-page.d.ts +5 -0
  177. package/dist/models/ps-scan-rendered-page.js +2 -0
  178. package/dist/models/ps-scan-stability.d.ts +8 -0
  179. package/dist/models/ps-scan-stability.js +2 -0
  180. package/dist/services/ps-scan-camera-service.d.ts +14 -0
  181. package/dist/services/ps-scan-camera-service.js +99 -0
  182. package/dist/services/ps-scan-canvas-service.d.ts +9 -0
  183. package/dist/services/ps-scan-canvas-service.js +56 -0
  184. package/dist/services/ps-scan-edge-service.d.ts +9 -0
  185. package/dist/services/ps-scan-edge-service.js +160 -0
  186. package/dist/services/ps-scan-enhance-service.d.ts +9 -0
  187. package/dist/services/ps-scan-enhance-service.js +101 -0
  188. package/dist/services/ps-scan-file-name-service.d.ts +6 -0
  189. package/dist/services/ps-scan-file-name-service.js +29 -0
  190. package/dist/services/ps-scan-geometry-service.d.ts +20 -0
  191. package/dist/services/ps-scan-geometry-service.js +120 -0
  192. package/dist/services/ps-scan-homography-service.d.ts +7 -0
  193. package/dist/services/ps-scan-homography-service.js +50 -0
  194. package/dist/services/ps-scan-hull-service.d.ts +6 -0
  195. package/dist/services/ps-scan-hull-service.js +63 -0
  196. package/dist/services/ps-scan-image-filter-service.d.ts +6 -0
  197. package/dist/services/ps-scan-image-filter-service.js +56 -0
  198. package/dist/services/ps-scan-order-service.d.ts +4 -0
  199. package/dist/services/ps-scan-order-service.js +17 -0
  200. package/dist/services/ps-scan-output-service.d.ts +5 -0
  201. package/dist/services/ps-scan-output-service.js +28 -0
  202. package/dist/services/ps-scan-pdf-service.d.ts +5 -0
  203. package/dist/services/ps-scan-pdf-service.js +60 -0
  204. package/dist/services/ps-scan-pipeline-service.d.ts +10 -0
  205. package/dist/services/ps-scan-pipeline-service.js +44 -0
  206. package/dist/services/ps-scan-platform-service.d.ts +4 -0
  207. package/dist/services/ps-scan-platform-service.js +19 -0
  208. package/dist/services/ps-scan-raster-service.d.ts +6 -0
  209. package/dist/services/ps-scan-raster-service.js +71 -0
  210. package/dist/services/ps-scan-stability-service.d.ts +9 -0
  211. package/dist/services/ps-scan-stability-service.js +45 -0
  212. package/dist/services/ps-scan-template-service.d.ts +3 -0
  213. package/dist/services/ps-scan-template-service.js +9 -0
  214. package/dist/services/ps-scan-warp-service.d.ts +6 -0
  215. package/dist/services/ps-scan-warp-service.js +39 -0
  216. package/package.json +9 -6
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface PsScanRaster {
2
+ data: Uint8ClampedArray<ArrayBuffer>;
3
+ height: number;
4
+ width: number;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface PsScanRenderedPage {
2
+ blob: Blob;
3
+ height: number;
4
+ width: number;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import type { PsScanQuad } from './ps-scan-quad';
2
+ export interface PsScanStability {
3
+ isArmed: boolean;
4
+ misses: number;
5
+ quad: PsScanQuad | null;
6
+ raw: PsScanQuad | null;
7
+ stableMs: number;
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,14 @@
1
+ import { PsScanCameraStatus } from '../constants/ps-scan-camera-status';
2
+ import type { PsScanCameraSession } from '../models/ps-scan-camera-session';
3
+ export declare class PsScanCameraService {
4
+ static attach(video: HTMLVideoElement, session: PsScanCameraSession): Promise<void>;
5
+ static check(): Promise<PsScanCameraStatus>;
6
+ static classify(error: unknown): PsScanCameraStatus;
7
+ static hasCamera(): Promise<boolean>;
8
+ static isSupported(): boolean;
9
+ static open(): Promise<PsScanCameraSession>;
10
+ static permission(): Promise<PermissionStatus | null>;
11
+ static setTorch(track: MediaStreamTrack, isOn: boolean): Promise<boolean>;
12
+ static stop(stream: MediaStream | null): void;
13
+ static supportsTorch(track: MediaStreamTrack | undefined): boolean;
14
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanCameraService = void 0;
4
+ const ps_scan_camera_status_1 = require("../constants/ps-scan-camera-status");
5
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
6
+ class PsScanCameraService {
7
+ static async attach(video, session) {
8
+ if (video.srcObject !== session.stream)
9
+ video.srcObject = session.stream;
10
+ await video.play().catch(() => undefined);
11
+ }
12
+ static async check() {
13
+ if (globalThis.isSecureContext === false)
14
+ return ps_scan_camera_status_1.PsScanCameraStatus.Insecure;
15
+ if (!PsScanCameraService.isSupported())
16
+ return ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
17
+ if (!(await PsScanCameraService.hasCamera()))
18
+ return ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
19
+ const permission = await PsScanCameraService.permission();
20
+ if (permission === null)
21
+ return ps_scan_camera_status_1.PsScanCameraStatus.Prompt;
22
+ if (permission.state === 'granted')
23
+ return ps_scan_camera_status_1.PsScanCameraStatus.Starting;
24
+ return permission.state === 'denied' ? ps_scan_camera_status_1.PsScanCameraStatus.Denied : ps_scan_camera_status_1.PsScanCameraStatus.Prompt;
25
+ }
26
+ static classify(error) {
27
+ const name = typeof error === 'object' && error !== null && 'name' in error ? String(error.name) : '';
28
+ if (name === 'NotAllowedError' || name === 'PermissionDeniedError')
29
+ return ps_scan_camera_status_1.PsScanCameraStatus.Denied;
30
+ if (name === 'SecurityError')
31
+ return ps_scan_camera_status_1.PsScanCameraStatus.Insecure;
32
+ if (name === 'NotReadableError' || name === 'TrackStartError' || name === 'AbortError')
33
+ return ps_scan_camera_status_1.PsScanCameraStatus.Busy;
34
+ return ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
35
+ }
36
+ static async hasCamera() {
37
+ if (typeof navigator.mediaDevices.enumerateDevices !== 'function')
38
+ return true;
39
+ try {
40
+ const devices = await navigator.mediaDevices.enumerateDevices();
41
+ return devices.length === 0 || devices.some((device) => device.kind === 'videoinput');
42
+ }
43
+ catch {
44
+ return true;
45
+ }
46
+ }
47
+ static isSupported() {
48
+ return (typeof navigator !== 'undefined' &&
49
+ typeof navigator.mediaDevices !== 'undefined' &&
50
+ typeof navigator.mediaDevices.getUserMedia === 'function');
51
+ }
52
+ static async open() {
53
+ const stream = await navigator.mediaDevices.getUserMedia({
54
+ audio: false,
55
+ video: {
56
+ facingMode: { ideal: 'environment' },
57
+ height: { ideal: 1440 },
58
+ width: { ideal: 1920 },
59
+ },
60
+ });
61
+ const track = stream.getVideoTracks()[0];
62
+ return { canTorch: PsScanCameraService.supportsTorch(track), stream, track };
63
+ }
64
+ static async permission() {
65
+ if (typeof navigator.permissions === 'undefined' ||
66
+ typeof navigator.permissions.query !== 'function')
67
+ return null;
68
+ try {
69
+ return await navigator.permissions.query({
70
+ name: ps_scan_config_1.PsScanConfig.cameraPermission,
71
+ });
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ static async setTorch(track, isOn) {
78
+ try {
79
+ await track.applyConstraints({
80
+ advanced: [{ [ps_scan_config_1.PsScanConfig.torchConstraint]: isOn }],
81
+ });
82
+ return isOn;
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
88
+ static stop(stream) {
89
+ if (stream === null)
90
+ return;
91
+ stream.getTracks().forEach((track) => track.stop());
92
+ }
93
+ static supportsTorch(track) {
94
+ if (track === undefined || typeof track.getCapabilities !== 'function')
95
+ return false;
96
+ return ps_scan_config_1.PsScanConfig.torchConstraint in track.getCapabilities();
97
+ }
98
+ }
99
+ exports.PsScanCameraService = PsScanCameraService;
@@ -0,0 +1,9 @@
1
+ import type { PsScanDecodedImage } from '../models/ps-scan-decoded-image';
2
+ import type { PsScanRaster } from '../models/ps-scan-raster';
3
+ export declare class PsScanCanvasService {
4
+ static decode(blob: Blob, maxDimension: number): Promise<PsScanDecodedImage>;
5
+ static encode(raster: PsScanRaster, quality: number): Promise<Blob>;
6
+ static fromVideo(video: HTMLVideoElement, quality: number): Promise<Blob>;
7
+ static sample(video: HTMLVideoElement, maxDimension: number): PsScanRaster;
8
+ private static context;
9
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanCanvasService = void 0;
4
+ const ps_scan_geometry_service_1 = require("./ps-scan-geometry-service");
5
+ class PsScanCanvasService {
6
+ static async decode(blob, maxDimension) {
7
+ const bitmap = await createImageBitmap(blob, { imageOrientation: 'from-image' });
8
+ const size = ps_scan_geometry_service_1.PsScanGeometryService.fitWithin({ height: bitmap.height, width: bitmap.width }, maxDimension);
9
+ const context = PsScanCanvasService.context(size.width, size.height);
10
+ context.drawImage(bitmap, 0, 0, size.width, size.height);
11
+ const naturalHeight = bitmap.height;
12
+ const naturalWidth = bitmap.width;
13
+ bitmap.close();
14
+ return {
15
+ naturalHeight,
16
+ naturalWidth,
17
+ raster: context.getImageData(0, 0, size.width, size.height),
18
+ };
19
+ }
20
+ static async encode(raster, quality) {
21
+ const context = PsScanCanvasService.context(raster.width, raster.height);
22
+ context.putImageData(new ImageData(raster.data, raster.width, raster.height), 0, 0);
23
+ const canvas = context.canvas;
24
+ if (canvas instanceof HTMLCanvasElement)
25
+ return await new Promise((resolve, reject) => canvas.toBlob((blob) => (blob === null ? reject(new Error('encode')) : resolve(blob)), 'image/jpeg', quality));
26
+ return await canvas.convertToBlob({ quality, type: 'image/jpeg' });
27
+ }
28
+ static async fromVideo(video, quality) {
29
+ const context = PsScanCanvasService.context(video.videoWidth, video.videoHeight);
30
+ context.drawImage(video, 0, 0);
31
+ return await PsScanCanvasService.encode(context.getImageData(0, 0, video.videoWidth, video.videoHeight), quality);
32
+ }
33
+ static sample(video, maxDimension) {
34
+ const size = ps_scan_geometry_service_1.PsScanGeometryService.fitWithin({ height: video.videoHeight, width: video.videoWidth }, maxDimension);
35
+ const context = PsScanCanvasService.context(size.width, size.height);
36
+ context.drawImage(video, 0, 0, size.width, size.height);
37
+ return context.getImageData(0, 0, size.width, size.height);
38
+ }
39
+ static context(width, height) {
40
+ if (typeof OffscreenCanvas !== 'undefined') {
41
+ const context = new OffscreenCanvas(width, height).getContext('2d', {
42
+ willReadFrequently: true,
43
+ });
44
+ if (context !== null)
45
+ return context;
46
+ }
47
+ const canvas = document.createElement('canvas');
48
+ canvas.width = width;
49
+ canvas.height = height;
50
+ const context = canvas.getContext('2d', { willReadFrequently: true });
51
+ if (context === null)
52
+ throw new Error('canvas');
53
+ return context;
54
+ }
55
+ }
56
+ exports.PsScanCanvasService = PsScanCanvasService;
@@ -0,0 +1,9 @@
1
+ import type { PsScanQuad } from '../models/ps-scan-quad';
2
+ import type { PsScanRaster } from '../models/ps-scan-raster';
3
+ export declare class PsScanEdgeService {
4
+ static detect(source: PsScanRaster): PsScanQuad | null;
5
+ private static dilate;
6
+ private static foreground;
7
+ private static largestComponent;
8
+ private static threshold;
9
+ }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanEdgeService = void 0;
4
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
5
+ const ps_scan_geometry_service_1 = require("./ps-scan-geometry-service");
6
+ const ps_scan_hull_service_1 = require("./ps-scan-hull-service");
7
+ const ps_scan_image_filter_service_1 = require("./ps-scan-image-filter-service");
8
+ const ps_scan_raster_service_1 = require("./ps-scan-raster-service");
9
+ class PsScanEdgeService {
10
+ static detect(source) {
11
+ const scale = Math.min(1, ps_scan_config_1.PsScanConfig.detectMaxDimension / Math.max(source.width, source.height));
12
+ const width = Math.max(8, Math.round(source.width * scale));
13
+ const height = Math.max(8, Math.round(source.height * scale));
14
+ const raster = ps_scan_raster_service_1.PsScanRasterService.resize(source, width, height);
15
+ const gray = ps_scan_image_filter_service_1.PsScanImageFilterService.blur(ps_scan_image_filter_service_1.PsScanImageFilterService.grayscale(raster), width, height);
16
+ const edges = ps_scan_image_filter_service_1.PsScanImageFilterService.sobel(gray, width, height);
17
+ let mask = PsScanEdgeService.threshold(edges);
18
+ for (let step = 0; step < ps_scan_config_1.PsScanConfig.detectDilations; step += 1)
19
+ mask = PsScanEdgeService.dilate(mask, width, height);
20
+ const foreground = PsScanEdgeService.foreground(mask, width, height);
21
+ const component = PsScanEdgeService.largestComponent(foreground, width, height);
22
+ if (component.length < 8)
23
+ return null;
24
+ const hull = ps_scan_hull_service_1.PsScanHullService.convexHull(component);
25
+ if (hull.length < 4)
26
+ return null;
27
+ const quad = ps_scan_geometry_service_1.PsScanGeometryService.order(ps_scan_hull_service_1.PsScanHullService.bestQuad(hull));
28
+ const imageArea = width * height;
29
+ const quadArea = ps_scan_geometry_service_1.PsScanGeometryService.area(quad);
30
+ const hullArea = ps_scan_geometry_service_1.PsScanGeometryService.area(hull);
31
+ if (quadArea / imageArea < ps_scan_config_1.PsScanConfig.detectMinAreaRatio)
32
+ return null;
33
+ if (quadArea / imageArea > ps_scan_config_1.PsScanConfig.detectMaxAreaRatio)
34
+ return null;
35
+ if (hullArea > 0 && quadArea / hullArea < ps_scan_config_1.PsScanConfig.detectMinHullFill)
36
+ return null;
37
+ if (!ps_scan_geometry_service_1.PsScanGeometryService.isConvex(quad))
38
+ return null;
39
+ return ps_scan_geometry_service_1.PsScanGeometryService.inset(quad, ps_scan_config_1.PsScanConfig.detectInset).map((point) => ({
40
+ x: Math.min(1, Math.max(0, point.x / (width - 1))),
41
+ y: Math.min(1, Math.max(0, point.y / (height - 1))),
42
+ }));
43
+ }
44
+ static dilate(mask, width, height) {
45
+ const out = new Uint8Array(mask.length);
46
+ for (let y = 0; y < height; y += 1)
47
+ for (let x = 0; x < width; x += 1) {
48
+ if (mask[y * width + x] === 0)
49
+ continue;
50
+ for (let dy = -1; dy <= 1; dy += 1)
51
+ for (let dx = -1; dx <= 1; dx += 1) {
52
+ const nx = x + dx;
53
+ const ny = y + dy;
54
+ if (nx >= 0 && ny >= 0 && nx < width && ny < height)
55
+ out[ny * width + nx] = 1;
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+ static foreground(mask, width, height) {
61
+ const background = new Uint8Array(mask.length);
62
+ const stack = [];
63
+ const seed = (index) => {
64
+ if (mask[index] === 0 && background[index] === 0) {
65
+ background[index] = 1;
66
+ stack.push(index);
67
+ }
68
+ };
69
+ for (let x = 0; x < width; x += 1) {
70
+ seed(x);
71
+ seed((height - 1) * width + x);
72
+ }
73
+ for (let y = 0; y < height; y += 1) {
74
+ seed(y * width);
75
+ seed(y * width + width - 1);
76
+ }
77
+ while (stack.length > 0) {
78
+ const index = stack.pop();
79
+ if (index === undefined)
80
+ break;
81
+ const x = index % width;
82
+ const y = (index - x) / width;
83
+ if (x > 0)
84
+ seed(index - 1);
85
+ if (x < width - 1)
86
+ seed(index + 1);
87
+ if (y > 0)
88
+ seed(index - width);
89
+ if (y < height - 1)
90
+ seed(index + width);
91
+ }
92
+ return background.map((value) => 1 - value);
93
+ }
94
+ static largestComponent(mask, width, height) {
95
+ const seen = new Uint8Array(mask.length);
96
+ let best = [];
97
+ let bestSize = 0;
98
+ for (let start = 0; start < mask.length; start += 1) {
99
+ if (mask[start] === 0 || seen[start] === 1)
100
+ continue;
101
+ const members = [];
102
+ const stack = [start];
103
+ let size = 0;
104
+ seen[start] = 1;
105
+ while (stack.length > 0) {
106
+ const index = stack.pop();
107
+ if (index === undefined)
108
+ break;
109
+ const x = index % width;
110
+ const y = (index - x) / width;
111
+ let isBoundary = false;
112
+ size += 1;
113
+ for (let dy = -1; dy <= 1; dy += 1)
114
+ for (let dx = -1; dx <= 1; dx += 1) {
115
+ if (dx === 0 && dy === 0)
116
+ continue;
117
+ const nx = x + dx;
118
+ const ny = y + dy;
119
+ if (nx < 0 || ny < 0 || nx >= width || ny >= height) {
120
+ isBoundary = true;
121
+ continue;
122
+ }
123
+ const next = ny * width + nx;
124
+ if (mask[next] === 0)
125
+ isBoundary = true;
126
+ else if (seen[next] === 0) {
127
+ seen[next] = 1;
128
+ stack.push(next);
129
+ }
130
+ }
131
+ if (isBoundary)
132
+ members.push({ x, y });
133
+ }
134
+ if (size > bestSize) {
135
+ bestSize = size;
136
+ best = members;
137
+ }
138
+ }
139
+ return best;
140
+ }
141
+ static threshold(edges) {
142
+ const bins = new Uint32Array(512);
143
+ edges.forEach((value) => {
144
+ bins[Math.min(511, value)] += 1;
145
+ });
146
+ const target = edges.length * ps_scan_config_1.PsScanConfig.detectPercentile;
147
+ let cumulative = 0;
148
+ let high = 0;
149
+ for (let bin = 0; bin < bins.length; bin += 1) {
150
+ cumulative += bins[bin];
151
+ if (cumulative >= target) {
152
+ high = bin;
153
+ break;
154
+ }
155
+ }
156
+ const limit = Math.max(ps_scan_config_1.PsScanConfig.detectMinEdgeStrength, high * ps_scan_config_1.PsScanConfig.detectRelativeStrength);
157
+ return Uint8Array.from(edges, (value) => (value >= limit ? 1 : 0));
158
+ }
159
+ }
160
+ exports.PsScanEdgeService = PsScanEdgeService;
@@ -0,0 +1,9 @@
1
+ import { PsScanFilter } from '../constants/ps-scan-filter';
2
+ import type { PsScanRaster } from '../models/ps-scan-raster';
3
+ export declare class PsScanEnhanceService {
4
+ static apply(source: PsScanRaster, filter: PsScanFilter): PsScanRaster;
5
+ private static colour;
6
+ private static document;
7
+ private static greyscale;
8
+ private static levels;
9
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanEnhanceService = void 0;
4
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
5
+ const ps_scan_filter_1 = require("../constants/ps-scan-filter");
6
+ const ps_scan_image_filter_service_1 = require("./ps-scan-image-filter-service");
7
+ const ps_scan_raster_service_1 = require("./ps-scan-raster-service");
8
+ class PsScanEnhanceService {
9
+ static apply(source, filter) {
10
+ if (filter === ps_scan_filter_1.PsScanFilter.Original)
11
+ return source;
12
+ if (filter === ps_scan_filter_1.PsScanFilter.Colour)
13
+ return PsScanEnhanceService.colour(source);
14
+ if (filter === ps_scan_filter_1.PsScanFilter.Greyscale)
15
+ return PsScanEnhanceService.greyscale(source);
16
+ return PsScanEnhanceService.document(source);
17
+ }
18
+ static colour(source) {
19
+ const gray = ps_scan_image_filter_service_1.PsScanImageFilterService.grayscale(source);
20
+ const { black, white } = PsScanEnhanceService.levels(gray);
21
+ const gain = 255 / Math.max(1, white - black);
22
+ const target = ps_scan_raster_service_1.PsScanRasterService.create(source.width, source.height);
23
+ for (let at = 0; at < source.data.length; at += 4) {
24
+ target.data[at] = (source.data[at] - black) * gain;
25
+ target.data[at + 1] = (source.data[at + 1] - black) * gain;
26
+ target.data[at + 2] = (source.data[at + 2] - black) * gain;
27
+ target.data[at + 3] = 255;
28
+ }
29
+ return target;
30
+ }
31
+ static document(source) {
32
+ const { height, width } = source;
33
+ const gray = ps_scan_image_filter_service_1.PsScanImageFilterService.grayscale(source);
34
+ const integral = new Float64Array((width + 1) * (height + 1));
35
+ for (let y = 0; y < height; y += 1) {
36
+ let row = 0;
37
+ for (let x = 0; x < width; x += 1) {
38
+ row += gray[y * width + x];
39
+ integral[(y + 1) * (width + 1) + x + 1] = integral[y * (width + 1) + x + 1] + row;
40
+ }
41
+ }
42
+ const half = Math.max(ps_scan_config_1.PsScanConfig.adaptiveWindowMin, Math.floor(Math.min(width, height) / ps_scan_config_1.PsScanConfig.adaptiveWindowDivisor)) >> 1;
43
+ const target = ps_scan_raster_service_1.PsScanRasterService.create(width, height);
44
+ for (let y = 0; y < height; y += 1) {
45
+ const y0 = Math.max(0, y - half);
46
+ const y1 = Math.min(height, y + half + 1);
47
+ for (let x = 0; x < width; x += 1) {
48
+ const x0 = Math.max(0, x - half);
49
+ const x1 = Math.min(width, x + half + 1);
50
+ const sum = integral[y1 * (width + 1) + x1] -
51
+ integral[y0 * (width + 1) + x1] -
52
+ integral[y1 * (width + 1) + x0] +
53
+ integral[y0 * (width + 1) + x0];
54
+ const mean = sum / ((x1 - x0) * (y1 - y0));
55
+ const value = gray[y * width + x] < mean * ps_scan_config_1.PsScanConfig.adaptiveRatio ? 0 : 255;
56
+ const at = (y * width + x) * 4;
57
+ target.data[at] = value;
58
+ target.data[at + 1] = value;
59
+ target.data[at + 2] = value;
60
+ target.data[at + 3] = 255;
61
+ }
62
+ }
63
+ return target;
64
+ }
65
+ static greyscale(source) {
66
+ const gray = ps_scan_image_filter_service_1.PsScanImageFilterService.grayscale(source);
67
+ const { black, white } = PsScanEnhanceService.levels(gray);
68
+ const gain = 255 / Math.max(1, white - black);
69
+ const target = ps_scan_raster_service_1.PsScanRasterService.create(source.width, source.height);
70
+ for (let index = 0; index < gray.length; index += 1) {
71
+ const value = (gray[index] - black) * gain;
72
+ const at = index * 4;
73
+ target.data[at] = value;
74
+ target.data[at + 1] = value;
75
+ target.data[at + 2] = value;
76
+ target.data[at + 3] = 255;
77
+ }
78
+ return target;
79
+ }
80
+ static levels(gray) {
81
+ const bins = new Uint32Array(256);
82
+ gray.forEach((value) => {
83
+ bins[value] += 1;
84
+ });
85
+ const find = (fraction) => {
86
+ const target = gray.length * fraction;
87
+ let cumulative = 0;
88
+ for (let bin = 0; bin < 256; bin += 1) {
89
+ cumulative += bins[bin];
90
+ if (cumulative >= target)
91
+ return bin;
92
+ }
93
+ return 255;
94
+ };
95
+ return {
96
+ black: find(ps_scan_config_1.PsScanConfig.levelLowPercentile),
97
+ white: find(ps_scan_config_1.PsScanConfig.levelHighPercentile),
98
+ };
99
+ }
100
+ }
101
+ exports.PsScanEnhanceService = PsScanEnhanceService;
@@ -0,0 +1,6 @@
1
+ import { PsScanOutputFormat } from '../constants/ps-scan-output-format';
2
+ export declare class PsScanFileNameService {
3
+ static build(base: string, format: PsScanOutputFormat, index: number, count: number): string;
4
+ static mimeType(format: PsScanOutputFormat): string;
5
+ static sanitize(base: string): string;
6
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanFileNameService = void 0;
4
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
5
+ const ps_scan_output_format_1 = require("../constants/ps-scan-output-format");
6
+ class PsScanFileNameService {
7
+ static build(base, format, index, count) {
8
+ const stem = PsScanFileNameService.sanitize(base);
9
+ if (format === ps_scan_output_format_1.PsScanOutputFormat.Pdf)
10
+ return `${stem}.pdf`;
11
+ if (count <= 1)
12
+ return `${stem}.jpg`;
13
+ return `${stem}-${String(index + 1).padStart(String(count).length, '0')}.jpg`;
14
+ }
15
+ static mimeType(format) {
16
+ return format === ps_scan_output_format_1.PsScanOutputFormat.Pdf ? 'application/pdf' : 'image/jpeg';
17
+ }
18
+ static sanitize(base) {
19
+ const cleaned = Array.from(base)
20
+ .filter((char) => char.charCodeAt(0) >= 32)
21
+ .join('')
22
+ .replace(/\.(pdf|jpe?g)$/i, '')
23
+ .replace(/[\\/:*?"<>|]+/g, '-')
24
+ .replace(/\s+/g, ' ')
25
+ .replace(/^[\s.-]+|[\s.-]+$/g, '');
26
+ return cleaned.length === 0 ? ps_scan_config_1.PsScanConfig.defaultFileName : cleaned.slice(0, 80);
27
+ }
28
+ }
29
+ exports.PsScanFileNameService = PsScanFileNameService;
@@ -0,0 +1,20 @@
1
+ import type { PsPageSize } from '../models/ps-page-size';
2
+ import type { PsPoint } from '../models/ps-point';
3
+ import type { PsScanQuad } from '../models/ps-scan-quad';
4
+ export declare class PsScanGeometryService {
5
+ static area(points: PsPoint[]): number;
6
+ static clamp(point: PsPoint): PsPoint;
7
+ static cover(container: PsPageSize, content: PsPageSize): PsPageSize;
8
+ static defaultQuad(): PsScanQuad;
9
+ static distance(from: PsPoint, to: PsPoint): number;
10
+ static fit(container: PsPageSize, content: PsPageSize): PsPageSize;
11
+ static fitWithin(size: PsPageSize, maxDimension: number): PsPageSize;
12
+ static inset(quad: PsScanQuad, distance: number): PsScanQuad;
13
+ static isConvex(points: PsPoint[]): boolean;
14
+ static isUsable(quad: PsScanQuad): boolean;
15
+ static nudge(quad: PsScanQuad, index: number, dx: number, dy: number): PsScanQuad;
16
+ static order(points: PsPoint[]): PsScanQuad;
17
+ static outputSize(quad: PsScanQuad, raster: PsPageSize): PsPageSize;
18
+ static rotateSize(size: PsPageSize, rotation: number): PsPageSize;
19
+ static toPixels(quad: PsScanQuad, size: PsPageSize): PsScanQuad;
20
+ }