@pallasoft/ps-file-components 1.2.4 → 1.4.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.
- package/dist/components/ps-document-scanner-loader.d.ts +1 -0
- package/dist/components/ps-document-scanner-loader.js +40 -0
- package/dist/components/ps-document-scanner-sheet.d.ts +2 -0
- package/dist/components/ps-document-scanner-sheet.js +138 -0
- package/dist/components/ps-document-scanner.d.ts +2 -0
- package/dist/components/ps-document-scanner.js +17 -0
- package/dist/components/ps-file-picker.d.ts +2 -1
- package/dist/components/ps-file-picker.js +2 -2
- package/dist/components/ps-file-viewer-pane.d.ts +2 -0
- package/dist/components/ps-file-viewer-pane.js +39 -0
- package/dist/components/ps-scan-camera-view.d.ts +17 -0
- package/dist/components/ps-scan-camera-view.js +27 -0
- package/dist/components/ps-scan-crop-editor.d.ts +13 -0
- package/dist/components/ps-scan-crop-editor.js +53 -0
- package/dist/components/ps-scan-icon-button.d.ts +10 -0
- package/dist/components/ps-scan-icon-button.js +7 -0
- package/dist/components/ps-scan-page-list.d.ts +12 -0
- package/dist/components/ps-scan-page-list.js +23 -0
- package/dist/components/ps-scan-review.d.ts +23 -0
- package/dist/components/ps-scan-review.js +20 -0
- package/dist/constants/ps-document-scanner-labels.d.ts +50 -0
- package/dist/constants/ps-document-scanner-labels.js +53 -0
- package/dist/constants/ps-file-picker-labels.d.ts +1 -0
- package/dist/constants/ps-file-picker-labels.js +1 -0
- package/dist/constants/ps-scan-camera-status.d.ts +7 -0
- package/dist/constants/ps-scan-camera-status.js +9 -0
- package/dist/constants/ps-scan-config.d.ts +31 -0
- package/dist/constants/ps-scan-config.js +34 -0
- package/dist/constants/ps-scan-corner-order.d.ts +1 -0
- package/dist/constants/ps-scan-corner-order.js +4 -0
- package/dist/constants/ps-scan-filter.d.ts +7 -0
- package/dist/constants/ps-scan-filter.js +9 -0
- package/dist/constants/ps-scan-output-format.d.ts +5 -0
- package/dist/constants/ps-scan-output-format.js +7 -0
- package/dist/constants/ps-scan-step.d.ts +6 -0
- package/dist/constants/ps-scan-step.js +8 -0
- package/dist/esm/components/ps-document-scanner-loader.js +4 -0
- package/dist/esm/components/ps-document-scanner-sheet.js +134 -0
- package/dist/esm/components/ps-document-scanner.js +13 -0
- package/dist/esm/components/ps-file-picker.js +3 -3
- package/dist/esm/components/ps-file-viewer-pane.js +35 -0
- package/dist/esm/components/ps-scan-camera-view.js +23 -0
- package/dist/esm/components/ps-scan-crop-editor.js +49 -0
- package/dist/esm/components/ps-scan-icon-button.js +3 -0
- package/dist/esm/components/ps-scan-page-list.js +19 -0
- package/dist/esm/components/ps-scan-review.js +16 -0
- package/dist/esm/constants/ps-document-scanner-labels.js +50 -0
- package/dist/esm/constants/ps-file-picker-labels.js +1 -0
- package/dist/esm/constants/ps-scan-camera-status.js +6 -0
- package/dist/esm/constants/ps-scan-config.js +31 -0
- package/dist/esm/constants/ps-scan-corner-order.js +1 -0
- package/dist/esm/constants/ps-scan-filter.js +6 -0
- package/dist/esm/constants/ps-scan-output-format.js +4 -0
- package/dist/esm/constants/ps-scan-step.js +5 -0
- package/dist/esm/hooks/use-ps-focus-trap.js +45 -0
- package/dist/esm/hooks/use-ps-scan-camera.js +62 -0
- package/dist/esm/hooks/use-ps-scan-pages.js +62 -0
- package/dist/esm/hooks/use-ps-scan-previews.js +57 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/models/ps-document-scanner-props.js +1 -0
- package/dist/esm/models/ps-document-scanner-sheet-props.js +1 -0
- package/dist/esm/models/ps-file-viewer-pane-props.js +1 -0
- package/dist/esm/models/ps-scan-added-page.js +1 -0
- package/dist/esm/models/ps-scan-camera-session.js +1 -0
- package/dist/esm/models/ps-scan-decoded-image.js +1 -0
- package/dist/esm/models/ps-scan-levels.js +1 -0
- package/dist/esm/models/ps-scan-output-options.js +1 -0
- package/dist/esm/models/ps-scan-page.js +1 -0
- package/dist/esm/models/ps-scan-pdf-page.js +1 -0
- package/dist/esm/models/ps-scan-prepared.js +1 -0
- package/dist/esm/models/ps-scan-quad.js +1 -0
- package/dist/esm/models/ps-scan-raster.js +1 -0
- package/dist/esm/models/ps-scan-rendered-page.js +1 -0
- package/dist/esm/services/ps-scan-camera-service.js +50 -0
- package/dist/esm/services/ps-scan-canvas-service.js +46 -0
- package/dist/esm/services/ps-scan-edge-service.js +156 -0
- package/dist/esm/services/ps-scan-enhance-service.js +97 -0
- package/dist/esm/services/ps-scan-file-name-service.js +25 -0
- package/dist/esm/services/ps-scan-geometry-service.js +96 -0
- package/dist/esm/services/ps-scan-homography-service.js +46 -0
- package/dist/esm/services/ps-scan-hull-service.js +59 -0
- package/dist/esm/services/ps-scan-image-filter-service.js +52 -0
- package/dist/esm/services/ps-scan-order-service.js +13 -0
- package/dist/esm/services/ps-scan-output-service.js +24 -0
- package/dist/esm/services/ps-scan-pdf-service.js +56 -0
- package/dist/esm/services/ps-scan-pipeline-service.js +40 -0
- package/dist/esm/services/ps-scan-raster-service.js +67 -0
- package/dist/esm/services/ps-scan-template-service.js +5 -0
- package/dist/esm/services/ps-scan-warp-service.js +35 -0
- package/dist/hooks/use-ps-focus-trap.d.ts +2 -0
- package/dist/hooks/use-ps-focus-trap.js +49 -0
- package/dist/hooks/use-ps-scan-camera.d.ts +9 -0
- package/dist/hooks/use-ps-scan-camera.js +66 -0
- package/dist/hooks/use-ps-scan-pages.d.ts +12 -0
- package/dist/hooks/use-ps-scan-pages.js +66 -0
- package/dist/hooks/use-ps-scan-previews.d.ts +2 -0
- package/dist/hooks/use-ps-scan-previews.js +61 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/models/ps-document-scanner-props.d.ts +15 -0
- package/dist/models/ps-document-scanner-props.js +2 -0
- package/dist/models/ps-document-scanner-sheet-props.d.ts +13 -0
- package/dist/models/ps-document-scanner-sheet-props.js +2 -0
- package/dist/models/ps-file-viewer-pane-props.d.ts +12 -0
- package/dist/models/ps-file-viewer-pane-props.js +2 -0
- package/dist/models/ps-scan-added-page.d.ts +5 -0
- package/dist/models/ps-scan-added-page.js +2 -0
- package/dist/models/ps-scan-camera-session.d.ts +5 -0
- package/dist/models/ps-scan-camera-session.js +2 -0
- package/dist/models/ps-scan-decoded-image.d.ts +6 -0
- package/dist/models/ps-scan-decoded-image.js +2 -0
- package/dist/models/ps-scan-levels.d.ts +4 -0
- package/dist/models/ps-scan-levels.js +2 -0
- package/dist/models/ps-scan-output-options.d.ts +7 -0
- package/dist/models/ps-scan-output-options.js +2 -0
- package/dist/models/ps-scan-page.d.ts +12 -0
- package/dist/models/ps-scan-page.js +2 -0
- package/dist/models/ps-scan-pdf-page.d.ts +5 -0
- package/dist/models/ps-scan-pdf-page.js +2 -0
- package/dist/models/ps-scan-prepared.d.ts +7 -0
- package/dist/models/ps-scan-prepared.js +2 -0
- package/dist/models/ps-scan-quad.d.ts +2 -0
- package/dist/models/ps-scan-quad.js +2 -0
- package/dist/models/ps-scan-raster.d.ts +5 -0
- package/dist/models/ps-scan-raster.js +2 -0
- package/dist/models/ps-scan-rendered-page.d.ts +5 -0
- package/dist/models/ps-scan-rendered-page.js +2 -0
- package/dist/services/ps-scan-camera-service.d.ts +10 -0
- package/dist/services/ps-scan-camera-service.js +54 -0
- package/dist/services/ps-scan-canvas-service.d.ts +8 -0
- package/dist/services/ps-scan-canvas-service.js +50 -0
- package/dist/services/ps-scan-edge-service.d.ts +9 -0
- package/dist/services/ps-scan-edge-service.js +160 -0
- package/dist/services/ps-scan-enhance-service.d.ts +9 -0
- package/dist/services/ps-scan-enhance-service.js +101 -0
- package/dist/services/ps-scan-file-name-service.d.ts +6 -0
- package/dist/services/ps-scan-file-name-service.js +29 -0
- package/dist/services/ps-scan-geometry-service.d.ts +18 -0
- package/dist/services/ps-scan-geometry-service.js +100 -0
- package/dist/services/ps-scan-homography-service.d.ts +7 -0
- package/dist/services/ps-scan-homography-service.js +50 -0
- package/dist/services/ps-scan-hull-service.d.ts +6 -0
- package/dist/services/ps-scan-hull-service.js +63 -0
- package/dist/services/ps-scan-image-filter-service.d.ts +6 -0
- package/dist/services/ps-scan-image-filter-service.js +56 -0
- package/dist/services/ps-scan-order-service.d.ts +4 -0
- package/dist/services/ps-scan-order-service.js +17 -0
- package/dist/services/ps-scan-output-service.d.ts +5 -0
- package/dist/services/ps-scan-output-service.js +28 -0
- package/dist/services/ps-scan-pdf-service.d.ts +5 -0
- package/dist/services/ps-scan-pdf-service.js +60 -0
- package/dist/services/ps-scan-pipeline-service.d.ts +10 -0
- package/dist/services/ps-scan-pipeline-service.js +44 -0
- package/dist/services/ps-scan-raster-service.d.ts +6 -0
- package/dist/services/ps-scan-raster-service.js +71 -0
- package/dist/services/ps-scan-template-service.d.ts +3 -0
- package/dist/services/ps-scan-template-service.js +9 -0
- package/dist/services/ps-scan-warp-service.d.ts +6 -0
- package/dist/services/ps-scan-warp-service.js +39 -0
- package/package.json +9 -6
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsFocusTrap = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const usePsFocusTrap = (ref, isActive, onEscape) => {
|
|
6
|
+
(0, react_1.useEffect)(() => {
|
|
7
|
+
if (!isActive || ref.current === null)
|
|
8
|
+
return;
|
|
9
|
+
const dialog = ref.current.closest('[role="dialog"]');
|
|
10
|
+
const root = dialog === null ? ref.current : dialog;
|
|
11
|
+
const focusable = () => Array.from(root.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter((element) => element.offsetParent !== null || element === document.activeElement);
|
|
12
|
+
if (!root.contains(document.activeElement)) {
|
|
13
|
+
root.setAttribute('tabindex', '-1');
|
|
14
|
+
root.focus({ preventScroll: true });
|
|
15
|
+
}
|
|
16
|
+
const onKeyDown = (event) => {
|
|
17
|
+
if (event.key === 'Escape') {
|
|
18
|
+
event.stopPropagation();
|
|
19
|
+
onEscape();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (event.key !== 'Tab')
|
|
23
|
+
return;
|
|
24
|
+
const items = focusable();
|
|
25
|
+
if (items.length === 0) {
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const first = items[0];
|
|
30
|
+
const last = items[items.length - 1];
|
|
31
|
+
const current = document.activeElement;
|
|
32
|
+
if (!root.contains(current)) {
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
first.focus();
|
|
35
|
+
}
|
|
36
|
+
else if (event.shiftKey && (current === first || current === root)) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
last.focus();
|
|
39
|
+
}
|
|
40
|
+
else if (!event.shiftKey && current === last) {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
first.focus();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
document.addEventListener('keydown', onKeyDown, true);
|
|
46
|
+
return () => document.removeEventListener('keydown', onKeyDown, true);
|
|
47
|
+
}, [isActive, onEscape]);
|
|
48
|
+
};
|
|
49
|
+
exports.usePsFocusTrap = usePsFocusTrap;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PsScanCameraStatus } from '../constants/ps-scan-camera-status';
|
|
2
|
+
export declare const usePsScanCamera: (isActive: boolean) => {
|
|
3
|
+
canTorch: boolean;
|
|
4
|
+
capture: () => Promise<Blob | null>;
|
|
5
|
+
isTorchOn: boolean;
|
|
6
|
+
status: PsScanCameraStatus;
|
|
7
|
+
toggleTorch: () => Promise<void>;
|
|
8
|
+
videoRef: import("react").RefObject<HTMLVideoElement>;
|
|
9
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsScanCamera = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ps_scan_camera_status_1 = require("../constants/ps-scan-camera-status");
|
|
6
|
+
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
7
|
+
const ps_scan_camera_service_1 = require("../services/ps-scan-camera-service");
|
|
8
|
+
const ps_scan_canvas_service_1 = require("../services/ps-scan-canvas-service");
|
|
9
|
+
const usePsScanCamera = (isActive) => {
|
|
10
|
+
const videoRef = (0, react_1.useRef)(null);
|
|
11
|
+
const sessionRef = (0, react_1.useRef)(null);
|
|
12
|
+
const [status, setStatus] = (0, react_1.useState)(ps_scan_camera_status_1.PsScanCameraStatus.Starting);
|
|
13
|
+
const [canTorch, setCanTorch] = (0, react_1.useState)(false);
|
|
14
|
+
const [isTorchOn, setIsTorchOn] = (0, react_1.useState)(false);
|
|
15
|
+
(0, react_1.useEffect)(() => {
|
|
16
|
+
if (!isActive)
|
|
17
|
+
return;
|
|
18
|
+
let isCancelled = false;
|
|
19
|
+
setIsTorchOn(false);
|
|
20
|
+
if (!ps_scan_camera_service_1.PsScanCameraService.isSupported()) {
|
|
21
|
+
setStatus(ps_scan_camera_status_1.PsScanCameraStatus.Unavailable);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
setStatus(ps_scan_camera_status_1.PsScanCameraStatus.Starting);
|
|
25
|
+
ps_scan_camera_service_1.PsScanCameraService.open()
|
|
26
|
+
.then(async (session) => {
|
|
27
|
+
if (isCancelled) {
|
|
28
|
+
ps_scan_camera_service_1.PsScanCameraService.stop(session.stream);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
sessionRef.current = session;
|
|
32
|
+
setCanTorch(session.canTorch);
|
|
33
|
+
const video = videoRef.current;
|
|
34
|
+
if (video !== null) {
|
|
35
|
+
video.srcObject = session.stream;
|
|
36
|
+
await video.play().catch(() => undefined);
|
|
37
|
+
}
|
|
38
|
+
setStatus(ps_scan_camera_status_1.PsScanCameraStatus.Ready);
|
|
39
|
+
})
|
|
40
|
+
.catch((error) => {
|
|
41
|
+
if (!isCancelled)
|
|
42
|
+
setStatus(ps_scan_camera_service_1.PsScanCameraService.classify(error));
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
isCancelled = true;
|
|
46
|
+
ps_scan_camera_service_1.PsScanCameraService.stop(sessionRef.current === null ? null : sessionRef.current.stream);
|
|
47
|
+
sessionRef.current = null;
|
|
48
|
+
if (videoRef.current !== null)
|
|
49
|
+
videoRef.current.srcObject = null;
|
|
50
|
+
};
|
|
51
|
+
}, [isActive]);
|
|
52
|
+
const toggleTorch = (0, react_1.useCallback)(async () => {
|
|
53
|
+
const session = sessionRef.current;
|
|
54
|
+
if (session === null)
|
|
55
|
+
return;
|
|
56
|
+
setIsTorchOn(await ps_scan_camera_service_1.PsScanCameraService.setTorch(session.track, !isTorchOn));
|
|
57
|
+
}, [isTorchOn]);
|
|
58
|
+
const capture = (0, react_1.useCallback)(async () => {
|
|
59
|
+
const video = videoRef.current;
|
|
60
|
+
if (video === null || video.videoWidth === 0)
|
|
61
|
+
return null;
|
|
62
|
+
return await ps_scan_canvas_service_1.PsScanCanvasService.fromVideo(video, ps_scan_config_1.PsScanConfig.capturedJpegQuality);
|
|
63
|
+
}, []);
|
|
64
|
+
return { canTorch, capture, isTorchOn, status, toggleTorch, videoRef };
|
|
65
|
+
};
|
|
66
|
+
exports.usePsScanCamera = usePsScanCamera;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PsScanAddedPage } from '../models/ps-scan-added-page';
|
|
2
|
+
import type { PsScanPage } from '../models/ps-scan-page';
|
|
3
|
+
import type { PsScanQuad } from '../models/ps-scan-quad';
|
|
4
|
+
export declare const usePsScanPages: () => {
|
|
5
|
+
add: (sources: Blob[]) => Promise<PsScanAddedPage[]>;
|
|
6
|
+
move: (from: number, to: number) => void;
|
|
7
|
+
pages: PsScanPage[];
|
|
8
|
+
remove: (id: string) => void;
|
|
9
|
+
replace: (id: string, source: Blob) => Promise<PsScanAddedPage>;
|
|
10
|
+
setCorners: (id: string, corners: PsScanQuad) => void;
|
|
11
|
+
update: (id: string, patch: Partial<Pick<PsScanPage, "corners" | "filter" | "rotation">>) => void;
|
|
12
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsScanPages = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ps_scan_filter_1 = require("../constants/ps-scan-filter");
|
|
6
|
+
const ps_scan_order_service_1 = require("../services/ps-scan-order-service");
|
|
7
|
+
const ps_scan_pipeline_service_1 = require("../services/ps-scan-pipeline-service");
|
|
8
|
+
const usePsScanPages = () => {
|
|
9
|
+
const [pages, setPages] = (0, react_1.useState)([]);
|
|
10
|
+
const urls = (0, react_1.useRef)(new Set());
|
|
11
|
+
const counter = (0, react_1.useRef)(0);
|
|
12
|
+
(0, react_1.useEffect)(() => () => {
|
|
13
|
+
urls.current.forEach((url) => URL.revokeObjectURL(url));
|
|
14
|
+
urls.current.clear();
|
|
15
|
+
}, []);
|
|
16
|
+
const build = (0, react_1.useCallback)(async (source) => {
|
|
17
|
+
const prepared = await ps_scan_pipeline_service_1.PsScanPipelineService.prepare(source);
|
|
18
|
+
const sourceUrl = URL.createObjectURL(source);
|
|
19
|
+
counter.current += 1;
|
|
20
|
+
urls.current.add(sourceUrl);
|
|
21
|
+
return {
|
|
22
|
+
isDetected: prepared.isDetected,
|
|
23
|
+
page: {
|
|
24
|
+
corners: prepared.corners,
|
|
25
|
+
filter: ps_scan_filter_1.PsScanFilter.Document,
|
|
26
|
+
height: prepared.height,
|
|
27
|
+
id: `scan-page-${counter.current}`,
|
|
28
|
+
rotation: 0,
|
|
29
|
+
source,
|
|
30
|
+
sourceUrl,
|
|
31
|
+
width: prepared.width,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
const add = (0, react_1.useCallback)(async (sources) => {
|
|
36
|
+
const built = [];
|
|
37
|
+
for (const source of sources)
|
|
38
|
+
built.push(await build(source));
|
|
39
|
+
setPages((current) => [...current, ...built.map((item) => item.page)]);
|
|
40
|
+
return built;
|
|
41
|
+
}, [build]);
|
|
42
|
+
const replace = (0, react_1.useCallback)(async (id, source) => {
|
|
43
|
+
const built = await build(source);
|
|
44
|
+
setPages((current) => current.map((page) => {
|
|
45
|
+
if (page.id !== id)
|
|
46
|
+
return page;
|
|
47
|
+
URL.revokeObjectURL(page.sourceUrl);
|
|
48
|
+
urls.current.delete(page.sourceUrl);
|
|
49
|
+
return { ...built.page, filter: page.filter, id: page.id };
|
|
50
|
+
}));
|
|
51
|
+
return built;
|
|
52
|
+
}, [build]);
|
|
53
|
+
const update = (0, react_1.useCallback)((id, patch) => setPages((current) => current.map((page) => (page.id === id ? { ...page, ...patch } : page))), []);
|
|
54
|
+
const setCorners = (0, react_1.useCallback)((id, corners) => update(id, { corners }), [update]);
|
|
55
|
+
const remove = (0, react_1.useCallback)((id) => setPages((current) => {
|
|
56
|
+
const target = current.find((page) => page.id === id);
|
|
57
|
+
if (target !== undefined) {
|
|
58
|
+
URL.revokeObjectURL(target.sourceUrl);
|
|
59
|
+
urls.current.delete(target.sourceUrl);
|
|
60
|
+
}
|
|
61
|
+
return current.filter((page) => page.id !== id);
|
|
62
|
+
}), []);
|
|
63
|
+
const move = (0, react_1.useCallback)((from, to) => setPages((current) => ps_scan_order_service_1.PsScanOrderService.move(current, from, to)), []);
|
|
64
|
+
return { add, move, pages, remove, replace, setCorners, update };
|
|
65
|
+
};
|
|
66
|
+
exports.usePsScanPages = usePsScanPages;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsScanPreviews = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
6
|
+
const ps_scan_canvas_service_1 = require("../services/ps-scan-canvas-service");
|
|
7
|
+
const ps_scan_pipeline_service_1 = require("../services/ps-scan-pipeline-service");
|
|
8
|
+
const usePsScanPreviews = (pages) => {
|
|
9
|
+
const rasters = (0, react_1.useRef)(new Map());
|
|
10
|
+
const urls = (0, react_1.useRef)(new Map());
|
|
11
|
+
const [previews, setPreviews] = (0, react_1.useState)({});
|
|
12
|
+
const signature = pages
|
|
13
|
+
.map((page) => `${page.id}:${page.filter}:${page.rotation}:${page.corners.map((point) => `${point.x.toFixed(4)},${point.y.toFixed(4)}`).join(';')}`)
|
|
14
|
+
.join('|');
|
|
15
|
+
(0, react_1.useEffect)(() => {
|
|
16
|
+
let isCancelled = false;
|
|
17
|
+
const timer = setTimeout(async () => {
|
|
18
|
+
const stale = [];
|
|
19
|
+
for (const page of pages) {
|
|
20
|
+
if (isCancelled)
|
|
21
|
+
return;
|
|
22
|
+
let raster = rasters.current.get(page.id);
|
|
23
|
+
if (raster === undefined) {
|
|
24
|
+
raster = await ps_scan_pipeline_service_1.PsScanPipelineService.measure(page.source);
|
|
25
|
+
rasters.current.set(page.id, raster);
|
|
26
|
+
}
|
|
27
|
+
const processed = ps_scan_pipeline_service_1.PsScanPipelineService.process(page, raster, ps_scan_config_1.PsScanConfig.previewMaxDimension);
|
|
28
|
+
const blob = await ps_scan_canvas_service_1.PsScanCanvasService.encode(processed, ps_scan_config_1.PsScanConfig.defaultJpegQuality);
|
|
29
|
+
if (isCancelled)
|
|
30
|
+
return;
|
|
31
|
+
const previous = urls.current.get(page.id);
|
|
32
|
+
if (previous !== undefined)
|
|
33
|
+
stale.push(previous);
|
|
34
|
+
urls.current.set(page.id, URL.createObjectURL(blob));
|
|
35
|
+
}
|
|
36
|
+
if (isCancelled)
|
|
37
|
+
return;
|
|
38
|
+
const ids = new Set(pages.map((page) => page.id));
|
|
39
|
+
urls.current.forEach((url, id) => {
|
|
40
|
+
if (ids.has(id))
|
|
41
|
+
return;
|
|
42
|
+
stale.push(url);
|
|
43
|
+
urls.current.delete(id);
|
|
44
|
+
rasters.current.delete(id);
|
|
45
|
+
});
|
|
46
|
+
setPreviews(Object.fromEntries(urls.current));
|
|
47
|
+
setTimeout(() => stale.forEach((url) => URL.revokeObjectURL(url)), 1000);
|
|
48
|
+
}, ps_scan_config_1.PsScanConfig.previewDebounceMs);
|
|
49
|
+
return () => {
|
|
50
|
+
isCancelled = true;
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
};
|
|
53
|
+
}, [signature]);
|
|
54
|
+
(0, react_1.useEffect)(() => () => {
|
|
55
|
+
urls.current.forEach((url) => URL.revokeObjectURL(url));
|
|
56
|
+
urls.current.clear();
|
|
57
|
+
rasters.current.clear();
|
|
58
|
+
}, []);
|
|
59
|
+
return previews;
|
|
60
|
+
};
|
|
61
|
+
exports.usePsScanPreviews = usePsScanPreviews;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './components/ps-file-list';
|
|
|
5
5
|
export * from './components/ps-file-picker';
|
|
6
6
|
export * from './components/ps-file-viewer-button';
|
|
7
7
|
export * from './components/ps-file-viewer-content';
|
|
8
|
+
export * from './components/ps-file-viewer-pane';
|
|
8
9
|
export * from './components/ps-file-viewer';
|
|
9
10
|
export * from './components/ps-highlighted-text';
|
|
10
11
|
export * from './components/ps-image-viewer';
|
|
@@ -39,6 +40,7 @@ export * from './models/ps-file-selection';
|
|
|
39
40
|
export * from './models/ps-file-text-state';
|
|
40
41
|
export * from './models/ps-file-viewer-button-tone';
|
|
41
42
|
export * from './models/ps-file-viewer-content-props';
|
|
43
|
+
export * from './models/ps-file-viewer-pane-props';
|
|
42
44
|
export * from './models/ps-label-set';
|
|
43
45
|
export * from './models/ps-page-size';
|
|
44
46
|
export * from './models/ps-pdf-match';
|
|
@@ -67,3 +69,8 @@ export * from './hooks/use-ps-active-match-scroll';
|
|
|
67
69
|
export * from './hooks/use-ps-file-text';
|
|
68
70
|
export * from './hooks/use-ps-in-view';
|
|
69
71
|
export * from './hooks/use-ps-upload-queue';
|
|
72
|
+
export * from './components/ps-document-scanner';
|
|
73
|
+
export * from './constants/ps-document-scanner-labels';
|
|
74
|
+
export * from './constants/ps-scan-filter';
|
|
75
|
+
export * from './constants/ps-scan-output-format';
|
|
76
|
+
export * from './models/ps-document-scanner-props';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ __exportStar(require("./components/ps-file-list"), exports);
|
|
|
21
21
|
__exportStar(require("./components/ps-file-picker"), exports);
|
|
22
22
|
__exportStar(require("./components/ps-file-viewer-button"), exports);
|
|
23
23
|
__exportStar(require("./components/ps-file-viewer-content"), exports);
|
|
24
|
+
__exportStar(require("./components/ps-file-viewer-pane"), exports);
|
|
24
25
|
__exportStar(require("./components/ps-file-viewer"), exports);
|
|
25
26
|
__exportStar(require("./components/ps-highlighted-text"), exports);
|
|
26
27
|
__exportStar(require("./components/ps-image-viewer"), exports);
|
|
@@ -55,6 +56,7 @@ __exportStar(require("./models/ps-file-selection"), exports);
|
|
|
55
56
|
__exportStar(require("./models/ps-file-text-state"), exports);
|
|
56
57
|
__exportStar(require("./models/ps-file-viewer-button-tone"), exports);
|
|
57
58
|
__exportStar(require("./models/ps-file-viewer-content-props"), exports);
|
|
59
|
+
__exportStar(require("./models/ps-file-viewer-pane-props"), exports);
|
|
58
60
|
__exportStar(require("./models/ps-label-set"), exports);
|
|
59
61
|
__exportStar(require("./models/ps-page-size"), exports);
|
|
60
62
|
__exportStar(require("./models/ps-pdf-match"), exports);
|
|
@@ -83,3 +85,8 @@ __exportStar(require("./hooks/use-ps-active-match-scroll"), exports);
|
|
|
83
85
|
__exportStar(require("./hooks/use-ps-file-text"), exports);
|
|
84
86
|
__exportStar(require("./hooks/use-ps-in-view"), exports);
|
|
85
87
|
__exportStar(require("./hooks/use-ps-upload-queue"), exports);
|
|
88
|
+
__exportStar(require("./components/ps-document-scanner"), exports);
|
|
89
|
+
__exportStar(require("./constants/ps-document-scanner-labels"), exports);
|
|
90
|
+
__exportStar(require("./constants/ps-scan-filter"), exports);
|
|
91
|
+
__exportStar(require("./constants/ps-scan-output-format"), exports);
|
|
92
|
+
__exportStar(require("./models/ps-document-scanner-props"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
|
+
import type { PsScanOutputFormat } from '../constants/ps-scan-output-format';
|
|
3
|
+
import type { PsLabelSet } from './ps-label-set';
|
|
4
|
+
export interface PsDocumentScannerProps {
|
|
5
|
+
className?: string;
|
|
6
|
+
fileName?: string;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
jpegQuality?: number;
|
|
9
|
+
labels?: Partial<PsLabelSet<typeof PsDocumentScannerLabels>>;
|
|
10
|
+
maxDimension?: number;
|
|
11
|
+
maxPages?: number;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
onComplete: (files: File[]) => void;
|
|
14
|
+
outputFormat?: PsScanOutputFormat;
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
|
+
import type { PsScanOutputFormat } from '../constants/ps-scan-output-format';
|
|
3
|
+
import type { PsLabelSet } from './ps-label-set';
|
|
4
|
+
export interface PsDocumentScannerSheetProps {
|
|
5
|
+
fileName: string;
|
|
6
|
+
jpegQuality: number;
|
|
7
|
+
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
8
|
+
maxDimension: number;
|
|
9
|
+
maxPages: number;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
onComplete: (files: File[]) => void;
|
|
12
|
+
outputFormat: PsScanOutputFormat;
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { PsFileViewerLabels } from '../constants/ps-file-viewer-labels';
|
|
3
|
+
import type { PsLabelSet } from './ps-label-set';
|
|
4
|
+
export interface PsFileViewerPaneProps {
|
|
5
|
+
contentType: string;
|
|
6
|
+
headerActions?: ReactNode;
|
|
7
|
+
id?: string;
|
|
8
|
+
labels?: PsLabelSet<typeof PsFileViewerLabels>;
|
|
9
|
+
name: string;
|
|
10
|
+
pdfWorkerSrc?: string;
|
|
11
|
+
url: string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PsScanFilter } from '../constants/ps-scan-filter';
|
|
2
|
+
import type { PsScanQuad } from './ps-scan-quad';
|
|
3
|
+
export interface PsScanPage {
|
|
4
|
+
corners: PsScanQuad;
|
|
5
|
+
filter: PsScanFilter;
|
|
6
|
+
height: number;
|
|
7
|
+
id: string;
|
|
8
|
+
rotation: number;
|
|
9
|
+
source: Blob;
|
|
10
|
+
sourceUrl: string;
|
|
11
|
+
width: number;
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
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 classify(error: unknown): PsScanCameraStatus;
|
|
5
|
+
static isSupported(): boolean;
|
|
6
|
+
static open(): Promise<PsScanCameraSession>;
|
|
7
|
+
static setTorch(track: MediaStreamTrack, isOn: boolean): Promise<boolean>;
|
|
8
|
+
static stop(stream: MediaStream | null): void;
|
|
9
|
+
static supportsTorch(track: MediaStreamTrack | undefined): boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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 classify(error) {
|
|
8
|
+
const name = typeof error === 'object' && error !== null && 'name' in error ? String(error.name) : '';
|
|
9
|
+
return name === 'NotAllowedError' ||
|
|
10
|
+
name === 'SecurityError' ||
|
|
11
|
+
name === 'PermissionDeniedError'
|
|
12
|
+
? ps_scan_camera_status_1.PsScanCameraStatus.Denied
|
|
13
|
+
: ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
|
|
14
|
+
}
|
|
15
|
+
static isSupported() {
|
|
16
|
+
return (typeof navigator !== 'undefined' &&
|
|
17
|
+
typeof navigator.mediaDevices !== 'undefined' &&
|
|
18
|
+
typeof navigator.mediaDevices.getUserMedia === 'function');
|
|
19
|
+
}
|
|
20
|
+
static async open() {
|
|
21
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
22
|
+
audio: false,
|
|
23
|
+
video: {
|
|
24
|
+
facingMode: { ideal: 'environment' },
|
|
25
|
+
height: { ideal: 1440 },
|
|
26
|
+
width: { ideal: 1920 },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
const track = stream.getVideoTracks()[0];
|
|
30
|
+
return { canTorch: PsScanCameraService.supportsTorch(track), stream, track };
|
|
31
|
+
}
|
|
32
|
+
static async setTorch(track, isOn) {
|
|
33
|
+
try {
|
|
34
|
+
await track.applyConstraints({
|
|
35
|
+
advanced: [{ [ps_scan_config_1.PsScanConfig.torchConstraint]: isOn }],
|
|
36
|
+
});
|
|
37
|
+
return isOn;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
static stop(stream) {
|
|
44
|
+
if (stream === null)
|
|
45
|
+
return;
|
|
46
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
47
|
+
}
|
|
48
|
+
static supportsTorch(track) {
|
|
49
|
+
if (track === undefined || typeof track.getCapabilities !== 'function')
|
|
50
|
+
return false;
|
|
51
|
+
return ps_scan_config_1.PsScanConfig.torchConstraint in track.getCapabilities();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.PsScanCameraService = PsScanCameraService;
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
+
private static context;
|
|
8
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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 context(width, height) {
|
|
34
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
35
|
+
const context = new OffscreenCanvas(width, height).getContext('2d', {
|
|
36
|
+
willReadFrequently: true,
|
|
37
|
+
});
|
|
38
|
+
if (context !== null)
|
|
39
|
+
return context;
|
|
40
|
+
}
|
|
41
|
+
const canvas = document.createElement('canvas');
|
|
42
|
+
canvas.width = width;
|
|
43
|
+
canvas.height = height;
|
|
44
|
+
const context = canvas.getContext('2d', { willReadFrequently: true });
|
|
45
|
+
if (context === null)
|
|
46
|
+
throw new Error('canvas');
|
|
47
|
+
return context;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.PsScanCanvasService = PsScanCanvasService;
|