@pallasoft/ps-file-components 1.4.0 → 2.0.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 -1
- package/dist/components/ps-document-scanner-sheet.d.ts +1 -1
- package/dist/components/ps-document-scanner-sheet.js +64 -86
- package/dist/components/ps-document-scanner.d.ts +1 -1
- package/dist/components/ps-document-scanner.js +2 -3
- package/dist/components/ps-file-picker-actions.d.ts +10 -0
- package/dist/components/ps-file-picker-actions.js +21 -0
- package/dist/components/ps-file-picker.d.ts +7 -1
- package/dist/components/ps-file-picker.js +30 -6
- package/dist/components/ps-scan-button.d.ts +13 -0
- package/dist/components/ps-scan-button.js +9 -0
- package/dist/components/ps-scan-camera-view.d.ts +11 -8
- package/dist/components/ps-scan-camera-view.js +14 -16
- package/dist/components/ps-scan-crop-editor.d.ts +4 -4
- package/dist/components/ps-scan-crop-editor.js +24 -8
- package/dist/components/ps-scan-denied-steps.d.ts +6 -0
- package/dist/components/ps-scan-denied-steps.js +33 -0
- package/dist/components/ps-scan-icon-button.d.ts +4 -3
- package/dist/components/ps-scan-icon-button.js +3 -1
- package/dist/components/ps-scan-illustration.d.ts +1 -0
- package/dist/components/ps-scan-illustration.js +6 -0
- package/dist/components/ps-scan-loupe.d.ts +10 -0
- package/dist/components/ps-scan-loupe.js +31 -0
- package/dist/components/ps-scan-permission-view.d.ts +10 -0
- package/dist/components/ps-scan-permission-view.js +48 -0
- package/dist/components/ps-scan-photo-button.d.ts +7 -0
- package/dist/components/ps-scan-photo-button.js +20 -0
- package/dist/components/ps-scan-preview.d.ts +12 -0
- package/dist/components/ps-scan-preview.js +10 -0
- package/dist/components/ps-scan-shutter.d.ts +6 -0
- package/dist/components/ps-scan-shutter.js +6 -0
- package/dist/components/ps-scan-viewfinder-overlay.d.ts +8 -0
- package/dist/components/ps-scan-viewfinder-overlay.js +28 -0
- package/dist/components/ps-upload-row.js +1 -1
- package/dist/constants/ps-document-scanner-labels.d.ts +39 -38
- package/dist/constants/ps-document-scanner-labels.js +39 -38
- package/dist/constants/ps-file-picker-labels.d.ts +5 -3
- package/dist/constants/ps-file-picker-labels.js +5 -3
- package/dist/constants/ps-scan-camera-status.d.ts +4 -0
- package/dist/constants/ps-scan-camera-status.js +4 -0
- package/dist/constants/ps-scan-chrome-classes.d.ts +10 -0
- package/dist/constants/ps-scan-chrome-classes.js +13 -0
- package/dist/constants/ps-scan-config.d.ts +34 -24
- package/dist/constants/ps-scan-config.js +34 -24
- package/dist/constants/ps-scan-platform.d.ts +6 -0
- package/dist/constants/ps-scan-platform.js +8 -0
- package/dist/constants/ps-scan-step.d.ts +1 -1
- package/dist/constants/ps-scan-step.js +1 -1
- package/dist/esm/components/ps-document-scanner-sheet.js +67 -89
- package/dist/esm/components/ps-document-scanner.js +2 -3
- package/dist/esm/components/ps-file-picker-actions.js +17 -0
- package/dist/esm/components/ps-file-picker.js +31 -7
- package/dist/esm/components/ps-scan-button.js +5 -0
- package/dist/esm/components/ps-scan-camera-view.js +17 -19
- package/dist/esm/components/ps-scan-crop-editor.js +26 -10
- package/dist/esm/components/ps-scan-denied-steps.js +29 -0
- package/dist/esm/components/ps-scan-icon-button.js +3 -1
- package/dist/esm/components/ps-scan-illustration.js +2 -0
- package/dist/esm/components/ps-scan-loupe.js +27 -0
- package/dist/esm/components/ps-scan-permission-view.js +44 -0
- package/dist/esm/components/ps-scan-photo-button.js +16 -0
- package/dist/esm/components/ps-scan-preview.js +6 -0
- package/dist/esm/components/ps-scan-shutter.js +2 -0
- package/dist/esm/components/ps-scan-viewfinder-overlay.js +24 -0
- package/dist/esm/components/ps-upload-row.js +1 -1
- package/dist/esm/constants/ps-document-scanner-labels.js +39 -38
- package/dist/esm/constants/ps-file-picker-labels.js +5 -3
- package/dist/esm/constants/ps-scan-camera-status.js +4 -0
- package/dist/esm/constants/ps-scan-chrome-classes.js +10 -0
- package/dist/esm/constants/ps-scan-config.js +34 -24
- package/dist/esm/constants/ps-scan-platform.js +5 -0
- package/dist/esm/constants/ps-scan-step.js +1 -1
- package/dist/esm/hooks/use-ps-scan-camera.js +52 -15
- package/dist/esm/hooks/use-ps-scan-live-detection.js +41 -0
- package/dist/esm/hooks/use-ps-scan-photo.js +42 -0
- package/dist/esm/index.js +1 -2
- package/dist/esm/models/ps-scan-region.js +1 -0
- package/dist/esm/models/ps-scan-result.js +1 -0
- package/dist/esm/models/ps-scan-stability.js +1 -0
- package/dist/esm/services/ps-scan-camera-service.js +52 -7
- package/dist/esm/services/ps-scan-canny-service.js +93 -0
- package/dist/esm/services/ps-scan-canvas-service.js +13 -1
- package/dist/esm/services/ps-scan-edge-service.js +73 -137
- package/dist/esm/services/ps-scan-file-name-service.js +2 -11
- package/dist/esm/services/ps-scan-geometry-service.js +56 -0
- package/dist/esm/services/ps-scan-hull-service.js +0 -39
- package/dist/esm/services/ps-scan-image-filter-service.js +0 -21
- package/dist/esm/services/ps-scan-morphology-service.js +44 -0
- package/dist/esm/services/ps-scan-pipeline-service.js +9 -19
- package/dist/esm/services/ps-scan-platform-service.js +15 -0
- package/dist/esm/services/ps-scan-polygon-service.js +75 -0
- package/dist/esm/services/ps-scan-region-service.js +52 -0
- package/dist/esm/services/ps-scan-stability-service.js +28 -0
- package/dist/esm/services/ps-scan-warp-service.js +8 -2
- package/dist/esm/services/ps-scan-yield-service.js +5 -0
- package/dist/hooks/use-ps-scan-camera.d.ts +3 -1
- package/dist/hooks/use-ps-scan-camera.js +52 -15
- package/dist/hooks/use-ps-scan-live-detection.d.ts +3 -0
- package/dist/hooks/use-ps-scan-live-detection.js +45 -0
- package/dist/hooks/use-ps-scan-photo.d.ts +9 -0
- package/dist/hooks/use-ps-scan-photo.js +46 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/models/ps-document-scanner-props.d.ts +0 -3
- package/dist/models/ps-document-scanner-sheet-props.d.ts +0 -3
- package/dist/models/ps-file-picker-scan.d.ts +4 -0
- package/dist/models/ps-scan-gradient.d.ts +4 -0
- package/dist/models/ps-scan-live-detection.d.ts +4 -0
- package/dist/models/ps-scan-located.d.ts +5 -0
- package/dist/models/ps-scan-photo.d.ts +9 -0
- package/dist/models/ps-scan-region.d.ts +5 -0
- package/dist/models/ps-scan-region.js +2 -0
- package/dist/models/ps-scan-result.d.ts +6 -0
- package/dist/models/ps-scan-result.js +2 -0
- package/dist/models/ps-scan-stability.d.ts +5 -0
- package/dist/models/ps-scan-stability.js +2 -0
- package/dist/services/ps-scan-camera-service.d.ts +4 -0
- package/dist/services/ps-scan-camera-service.js +52 -7
- package/dist/services/ps-scan-canny-service.d.ts +7 -0
- package/dist/services/ps-scan-canny-service.js +97 -0
- package/dist/services/ps-scan-canvas-service.d.ts +1 -0
- package/dist/services/ps-scan-canvas-service.js +13 -1
- package/dist/services/ps-scan-edge-service.d.ts +5 -5
- package/dist/services/ps-scan-edge-service.js +73 -137
- package/dist/services/ps-scan-file-name-service.d.ts +1 -3
- package/dist/services/ps-scan-file-name-service.js +2 -11
- package/dist/services/ps-scan-geometry-service.d.ts +4 -0
- package/dist/services/ps-scan-geometry-service.js +56 -0
- package/dist/services/ps-scan-hull-service.d.ts +0 -2
- package/dist/services/ps-scan-hull-service.js +0 -39
- package/dist/services/ps-scan-image-filter-service.d.ts +0 -1
- package/dist/services/ps-scan-image-filter-service.js +0 -21
- package/dist/services/ps-scan-morphology-service.d.ts +7 -0
- package/dist/services/ps-scan-morphology-service.js +48 -0
- package/dist/services/ps-scan-pipeline-service.d.ts +2 -5
- package/dist/services/ps-scan-pipeline-service.js +9 -19
- package/dist/services/ps-scan-platform-service.d.ts +4 -0
- package/dist/services/ps-scan-platform-service.js +19 -0
- package/dist/services/ps-scan-polygon-service.d.ts +8 -0
- package/dist/services/ps-scan-polygon-service.js +79 -0
- package/dist/services/ps-scan-region-service.d.ts +4 -0
- package/dist/services/ps-scan-region-service.js +56 -0
- package/dist/services/ps-scan-stability-service.d.ts +7 -0
- package/dist/services/ps-scan-stability-service.js +32 -0
- package/dist/services/ps-scan-warp-service.d.ts +1 -1
- package/dist/services/ps-scan-warp-service.js +8 -2
- package/dist/services/ps-scan-yield-service.d.ts +3 -0
- package/dist/services/ps-scan-yield-service.js +9 -0
- package/package.json +3 -3
- package/dist/components/ps-scan-page-list.d.ts +0 -12
- package/dist/components/ps-scan-page-list.js +0 -23
- package/dist/components/ps-scan-review.d.ts +0 -23
- package/dist/components/ps-scan-review.js +0 -20
- package/dist/constants/ps-scan-filter.d.ts +0 -7
- package/dist/constants/ps-scan-filter.js +0 -9
- package/dist/constants/ps-scan-output-format.d.ts +0 -5
- package/dist/constants/ps-scan-output-format.js +0 -7
- package/dist/esm/components/ps-scan-page-list.js +0 -19
- package/dist/esm/components/ps-scan-review.js +0 -16
- package/dist/esm/constants/ps-scan-filter.js +0 -6
- package/dist/esm/constants/ps-scan-output-format.js +0 -4
- package/dist/esm/hooks/use-ps-scan-pages.js +0 -62
- package/dist/esm/hooks/use-ps-scan-previews.js +0 -57
- package/dist/esm/services/ps-scan-enhance-service.js +0 -97
- package/dist/esm/services/ps-scan-order-service.js +0 -13
- package/dist/esm/services/ps-scan-output-service.js +0 -24
- package/dist/esm/services/ps-scan-pdf-service.js +0 -56
- package/dist/hooks/use-ps-scan-pages.d.ts +0 -12
- package/dist/hooks/use-ps-scan-pages.js +0 -66
- package/dist/hooks/use-ps-scan-previews.d.ts +0 -2
- package/dist/hooks/use-ps-scan-previews.js +0 -61
- package/dist/models/ps-scan-added-page.d.ts +0 -5
- package/dist/models/ps-scan-levels.d.ts +0 -4
- package/dist/models/ps-scan-output-options.d.ts +0 -7
- package/dist/models/ps-scan-page.d.ts +0 -12
- package/dist/models/ps-scan-pdf-page.d.ts +0 -5
- package/dist/services/ps-scan-enhance-service.d.ts +0 -9
- package/dist/services/ps-scan-enhance-service.js +0 -101
- package/dist/services/ps-scan-order-service.d.ts +0 -4
- package/dist/services/ps-scan-order-service.js +0 -17
- package/dist/services/ps-scan-output-service.d.ts +0 -5
- package/dist/services/ps-scan-output-service.js +0 -28
- package/dist/services/ps-scan-pdf-service.d.ts +0 -5
- package/dist/services/ps-scan-pdf-service.js +0 -60
- /package/dist/esm/models/{ps-scan-added-page.js → ps-file-picker-scan.js} +0 -0
- /package/dist/esm/models/{ps-scan-levels.js → ps-scan-gradient.js} +0 -0
- /package/dist/esm/models/{ps-scan-output-options.js → ps-scan-live-detection.js} +0 -0
- /package/dist/esm/models/{ps-scan-page.js → ps-scan-located.js} +0 -0
- /package/dist/esm/models/{ps-scan-pdf-page.js → ps-scan-photo.js} +0 -0
- /package/dist/models/{ps-scan-added-page.js → ps-file-picker-scan.js} +0 -0
- /package/dist/models/{ps-scan-levels.js → ps-scan-gradient.js} +0 -0
- /package/dist/models/{ps-scan-output-options.js → ps-scan-live-detection.js} +0 -0
- /package/dist/models/{ps-scan-page.js → ps-scan-located.js} +0 -0
- /package/dist/models/{ps-scan-pdf-page.js → ps-scan-photo.js} +0 -0
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
|
-
import { PsScanFilter } from '../constants/ps-scan-filter';
|
|
3
|
-
import type { PsLabelSet } from '../models/ps-label-set';
|
|
4
|
-
import type { PsScanPage } from '../models/ps-scan-page';
|
|
5
|
-
export interface PsScanReviewProps {
|
|
6
|
-
canAddPage: boolean;
|
|
7
|
-
error: string;
|
|
8
|
-
isBusy: boolean;
|
|
9
|
-
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
10
|
-
onAddPage: () => void;
|
|
11
|
-
onDelete: () => void;
|
|
12
|
-
onEditCrop: () => void;
|
|
13
|
-
onFilter: (filter: PsScanFilter) => void;
|
|
14
|
-
onFinish: () => void;
|
|
15
|
-
onMove: (from: number, to: number) => void;
|
|
16
|
-
onRetake: () => void;
|
|
17
|
-
onRotate: () => void;
|
|
18
|
-
onSelect: (id: string) => void;
|
|
19
|
-
page: PsScanPage;
|
|
20
|
-
pages: PsScanPage[];
|
|
21
|
-
previews: Record<string, string>;
|
|
22
|
-
}
|
|
23
|
-
export declare const PsScanReview: ({ canAddPage, error, isBusy, labels, onAddPage, onDelete, onEditCrop, onFilter, onFinish, onMove, onRetake, onRotate, onSelect, page, pages, previews, }: PsScanReviewProps) => import("react").JSX.Element;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PsScanReview = void 0;
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
-
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
|
-
const ps_scan_filter_1 = require("../constants/ps-scan-filter");
|
|
8
|
-
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
9
|
-
const ps_scan_page_list_1 = require("./ps-scan-page-list");
|
|
10
|
-
const PsScanReview = ({ canAddPage, error, isBusy, labels, onAddPage, onDelete, onEditCrop, onFilter, onFinish, onMove, onRetake, onRotate, onSelect, page, pages, previews, }) => {
|
|
11
|
-
const filters = [
|
|
12
|
-
{ key: ps_scan_filter_1.PsScanFilter.Original, label: labels.filterOriginal },
|
|
13
|
-
{ key: ps_scan_filter_1.PsScanFilter.Colour, label: labels.filterColour },
|
|
14
|
-
{ key: ps_scan_filter_1.PsScanFilter.Greyscale, label: labels.filterGreyscale },
|
|
15
|
-
{ key: ps_scan_filter_1.PsScanFilter.Document, label: labels.filterDocument },
|
|
16
|
-
];
|
|
17
|
-
const preview = previews[page.id];
|
|
18
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 flex-col lg:flex-row', children: [(0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 flex-1 items-center justify-center bg-slate-950 p-3', children: preview === undefined ? ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'ps-skeleton h-full w-2/3 rounded' })) : ((0, jsx_runtime_1.jsx)("img", { alt: labels.page, className: 'max-h-full max-w-full object-contain', src: preview })) }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex max-h-[55%] min-h-0 shrink-0 flex-col gap-3 overflow-y-auto bg-surface px-4 pt-3 lg:max-h-none lg:w-80 lg:border-l lg:border-line', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center gap-2', children: [(0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { label: labels.rotate, onClick: onRotate, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: ps_icons_1.PsIconKey.Rotate }) }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { label: labels.editCrop, onClick: onEditCrop, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: ps_icons_1.PsIconKey.Pencil }) }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { label: labels.retake, onClick: onRetake, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: ps_icons_1.PsIconKey.Refresh }) }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { label: labels.deletePage, onClick: onDelete, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: ps_icons_1.PsIconKey.Trash }) })] }), (0, jsx_runtime_1.jsx)("div", { "aria-label": labels.filters, className: 'flex flex-wrap gap-2', role: 'group', children: filters.map((filter) => ((0, jsx_runtime_1.jsx)("button", { "aria-pressed": page.filter === filter.key, className: `ps-focus-ring min-h-11 rounded-ps-control border px-3 text-sm font-medium transition ${page.filter === filter.key ? 'border-primary bg-primary-soft text-primary' : 'border-line bg-surface text-ink hover:border-primary/40'}`, onClick: () => onFilter(filter.key), type: 'button', children: filter.label }, filter.key))) }), (0, jsx_runtime_1.jsxs)("section", { "aria-label": labels.pages, className: 'min-h-0', children: [(0, jsx_runtime_1.jsxs)("h3", { className: 'mb-2 text-sm font-semibold text-ink', children: [labels.pages, " (", pages.length, ")"] }), (0, jsx_runtime_1.jsx)(ps_scan_page_list_1.PsScanPageList, { activeId: page.id, labels: labels, onMove: onMove, onSelect: onSelect, pages: pages, previews: previews })] }), error === '' ? null : ((0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-danger', role: 'alert', children: error })), (0, jsx_runtime_1.jsxs)("div", { className: 'sticky bottom-0 mt-auto flex flex-wrap gap-3 bg-surface pt-2 pb-[max(0.75rem,env(safe-area-inset-bottom))]', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { className: 'flex-1', disabled: !canAddPage || isBusy, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Plus }), onClick: onAddPage, variant: 'surface', children: labels.addPage }), (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { className: 'flex-1', isLoading: isBusy, loadingLabel: labels.finishing, onClick: onFinish, variant: 'primary', children: labels.finish })] })] })] }));
|
|
19
|
-
};
|
|
20
|
-
exports.PsScanReview = PsScanReview;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { PsSortableList } from '@pallasoft/ps-core-components';
|
|
3
|
-
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
-
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
5
|
-
import { PsScanTemplateService } from '../services/ps-scan-template-service';
|
|
6
|
-
import { PsScanIconButton } from './ps-scan-icon-button';
|
|
7
|
-
export const PsScanPageList = ({ activeId, labels, onMove, onSelect, pages, previews, }) => {
|
|
8
|
-
const items = pages.map((page, index) => ({
|
|
9
|
-
id: page.id,
|
|
10
|
-
label: `${labels.page} ${index + 1}`,
|
|
11
|
-
}));
|
|
12
|
-
const say = (template, label, position) => PsScanTemplateService.fill(template, { count: pages.length, page: label, position });
|
|
13
|
-
return (_jsx(PsSortableList, { announcements: {
|
|
14
|
-
cancelled: (label) => say(labels.reorderCancelled, label, 0),
|
|
15
|
-
dropped: (label, position) => say(labels.reorderDropped, label, position),
|
|
16
|
-
lifted: (label, position) => say(labels.reorderLifted, label, position),
|
|
17
|
-
moved: (label, position) => say(labels.reorderMoved, label, position),
|
|
18
|
-
}, className: 'flex flex-col gap-2', getHandleLabel: (label) => PsScanTemplateService.fill(labels.dragPage, { page: label }), instructions: labels.reorderInstructions, items: items, onMove: (_id, from, to) => onMove(from, to), renderItem: ({ handle, index, isDragging, item }) => (_jsxs("div", { className: PallasoftClassNameService.merge('flex items-center gap-2 rounded-ps-control border bg-surface p-1.5', item.id === activeId ? 'border-primary' : 'border-line', isDragging ? 'opacity-80 shadow-lg' : ''), children: [handle, _jsxs("button", { "aria-current": item.id === activeId, "aria-label": item.label, className: 'ps-focus-ring flex min-h-11 min-w-0 flex-1 items-center gap-3 rounded-ps-control text-left', onClick: () => onSelect(item.id), type: 'button', children: [previews[item.id] === undefined ? (_jsx("span", { className: 'ps-skeleton h-14 w-10 shrink-0 rounded' })) : (_jsx("img", { alt: '', className: 'h-14 w-10 shrink-0 rounded border border-line bg-white object-cover object-top', src: previews[item.id] })), _jsx("span", { className: 'truncate text-sm font-medium text-ink', children: item.label })] }), _jsx(PsScanIconButton, { disabled: index === 0, label: PsScanTemplateService.fill(labels.moveUp, { page: item.label }), onClick: () => onMove(index, index - 1), children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5 -rotate-90', iconKey: PsIconKey.ChevronRight }) }), _jsx(PsScanIconButton, { disabled: index === pages.length - 1, label: PsScanTemplateService.fill(labels.moveDown, { page: item.label }), onClick: () => onMove(index, index + 1), children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5 rotate-90', iconKey: PsIconKey.ChevronRight }) })] })), role: 'list' }));
|
|
19
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { PsButton } from '@pallasoft/ps-core-components';
|
|
3
|
-
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
4
|
-
import { PsScanFilter } from '../constants/ps-scan-filter';
|
|
5
|
-
import { PsScanIconButton } from './ps-scan-icon-button';
|
|
6
|
-
import { PsScanPageList } from './ps-scan-page-list';
|
|
7
|
-
export const PsScanReview = ({ canAddPage, error, isBusy, labels, onAddPage, onDelete, onEditCrop, onFilter, onFinish, onMove, onRetake, onRotate, onSelect, page, pages, previews, }) => {
|
|
8
|
-
const filters = [
|
|
9
|
-
{ key: PsScanFilter.Original, label: labels.filterOriginal },
|
|
10
|
-
{ key: PsScanFilter.Colour, label: labels.filterColour },
|
|
11
|
-
{ key: PsScanFilter.Greyscale, label: labels.filterGreyscale },
|
|
12
|
-
{ key: PsScanFilter.Document, label: labels.filterDocument },
|
|
13
|
-
];
|
|
14
|
-
const preview = previews[page.id];
|
|
15
|
-
return (_jsxs("div", { className: 'flex h-full min-h-0 flex-col lg:flex-row', children: [_jsx("div", { className: 'flex min-h-0 flex-1 items-center justify-center bg-slate-950 p-3', children: preview === undefined ? (_jsx("span", { "aria-hidden": 'true', className: 'ps-skeleton h-full w-2/3 rounded' })) : (_jsx("img", { alt: labels.page, className: 'max-h-full max-w-full object-contain', src: preview })) }), _jsxs("div", { className: 'flex max-h-[55%] min-h-0 shrink-0 flex-col gap-3 overflow-y-auto bg-surface px-4 pt-3 lg:max-h-none lg:w-80 lg:border-l lg:border-line', children: [_jsxs("div", { className: 'flex flex-wrap items-center gap-2', children: [_jsx(PsScanIconButton, { label: labels.rotate, onClick: onRotate, children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: PsIconKey.Rotate }) }), _jsx(PsScanIconButton, { label: labels.editCrop, onClick: onEditCrop, children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: PsIconKey.Pencil }) }), _jsx(PsScanIconButton, { label: labels.retake, onClick: onRetake, children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: PsIconKey.Refresh }) }), _jsx(PsScanIconButton, { label: labels.deletePage, onClick: onDelete, children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: PsIconKey.Trash }) })] }), _jsx("div", { "aria-label": labels.filters, className: 'flex flex-wrap gap-2', role: 'group', children: filters.map((filter) => (_jsx("button", { "aria-pressed": page.filter === filter.key, className: `ps-focus-ring min-h-11 rounded-ps-control border px-3 text-sm font-medium transition ${page.filter === filter.key ? 'border-primary bg-primary-soft text-primary' : 'border-line bg-surface text-ink hover:border-primary/40'}`, onClick: () => onFilter(filter.key), type: 'button', children: filter.label }, filter.key))) }), _jsxs("section", { "aria-label": labels.pages, className: 'min-h-0', children: [_jsxs("h3", { className: 'mb-2 text-sm font-semibold text-ink', children: [labels.pages, " (", pages.length, ")"] }), _jsx(PsScanPageList, { activeId: page.id, labels: labels, onMove: onMove, onSelect: onSelect, pages: pages, previews: previews })] }), error === '' ? null : (_jsx("p", { className: 'text-sm text-danger', role: 'alert', children: error })), _jsxs("div", { className: 'sticky bottom-0 mt-auto flex flex-wrap gap-3 bg-surface pt-2 pb-[max(0.75rem,env(safe-area-inset-bottom))]', children: [_jsx(PsButton, { className: 'flex-1', disabled: !canAddPage || isBusy, leadingIcon: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Plus }), onClick: onAddPage, variant: 'surface', children: labels.addPage }), _jsx(PsButton, { className: 'flex-1', isLoading: isBusy, loadingLabel: labels.finishing, onClick: onFinish, variant: 'primary', children: labels.finish })] })] })] }));
|
|
16
|
-
};
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { PsScanFilter } from '../constants/ps-scan-filter';
|
|
3
|
-
import { PsScanOrderService } from '../services/ps-scan-order-service';
|
|
4
|
-
import { PsScanPipelineService } from '../services/ps-scan-pipeline-service';
|
|
5
|
-
export const usePsScanPages = () => {
|
|
6
|
-
const [pages, setPages] = useState([]);
|
|
7
|
-
const urls = useRef(new Set());
|
|
8
|
-
const counter = useRef(0);
|
|
9
|
-
useEffect(() => () => {
|
|
10
|
-
urls.current.forEach((url) => URL.revokeObjectURL(url));
|
|
11
|
-
urls.current.clear();
|
|
12
|
-
}, []);
|
|
13
|
-
const build = useCallback(async (source) => {
|
|
14
|
-
const prepared = await PsScanPipelineService.prepare(source);
|
|
15
|
-
const sourceUrl = URL.createObjectURL(source);
|
|
16
|
-
counter.current += 1;
|
|
17
|
-
urls.current.add(sourceUrl);
|
|
18
|
-
return {
|
|
19
|
-
isDetected: prepared.isDetected,
|
|
20
|
-
page: {
|
|
21
|
-
corners: prepared.corners,
|
|
22
|
-
filter: PsScanFilter.Document,
|
|
23
|
-
height: prepared.height,
|
|
24
|
-
id: `scan-page-${counter.current}`,
|
|
25
|
-
rotation: 0,
|
|
26
|
-
source,
|
|
27
|
-
sourceUrl,
|
|
28
|
-
width: prepared.width,
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}, []);
|
|
32
|
-
const add = useCallback(async (sources) => {
|
|
33
|
-
const built = [];
|
|
34
|
-
for (const source of sources)
|
|
35
|
-
built.push(await build(source));
|
|
36
|
-
setPages((current) => [...current, ...built.map((item) => item.page)]);
|
|
37
|
-
return built;
|
|
38
|
-
}, [build]);
|
|
39
|
-
const replace = useCallback(async (id, source) => {
|
|
40
|
-
const built = await build(source);
|
|
41
|
-
setPages((current) => current.map((page) => {
|
|
42
|
-
if (page.id !== id)
|
|
43
|
-
return page;
|
|
44
|
-
URL.revokeObjectURL(page.sourceUrl);
|
|
45
|
-
urls.current.delete(page.sourceUrl);
|
|
46
|
-
return { ...built.page, filter: page.filter, id: page.id };
|
|
47
|
-
}));
|
|
48
|
-
return built;
|
|
49
|
-
}, [build]);
|
|
50
|
-
const update = useCallback((id, patch) => setPages((current) => current.map((page) => (page.id === id ? { ...page, ...patch } : page))), []);
|
|
51
|
-
const setCorners = useCallback((id, corners) => update(id, { corners }), [update]);
|
|
52
|
-
const remove = useCallback((id) => setPages((current) => {
|
|
53
|
-
const target = current.find((page) => page.id === id);
|
|
54
|
-
if (target !== undefined) {
|
|
55
|
-
URL.revokeObjectURL(target.sourceUrl);
|
|
56
|
-
urls.current.delete(target.sourceUrl);
|
|
57
|
-
}
|
|
58
|
-
return current.filter((page) => page.id !== id);
|
|
59
|
-
}), []);
|
|
60
|
-
const move = useCallback((from, to) => setPages((current) => PsScanOrderService.move(current, from, to)), []);
|
|
61
|
-
return { add, move, pages, remove, replace, setCorners, update };
|
|
62
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { PsScanConfig } from '../constants/ps-scan-config';
|
|
3
|
-
import { PsScanCanvasService } from '../services/ps-scan-canvas-service';
|
|
4
|
-
import { PsScanPipelineService } from '../services/ps-scan-pipeline-service';
|
|
5
|
-
export const usePsScanPreviews = (pages) => {
|
|
6
|
-
const rasters = useRef(new Map());
|
|
7
|
-
const urls = useRef(new Map());
|
|
8
|
-
const [previews, setPreviews] = useState({});
|
|
9
|
-
const signature = pages
|
|
10
|
-
.map((page) => `${page.id}:${page.filter}:${page.rotation}:${page.corners.map((point) => `${point.x.toFixed(4)},${point.y.toFixed(4)}`).join(';')}`)
|
|
11
|
-
.join('|');
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
let isCancelled = false;
|
|
14
|
-
const timer = setTimeout(async () => {
|
|
15
|
-
const stale = [];
|
|
16
|
-
for (const page of pages) {
|
|
17
|
-
if (isCancelled)
|
|
18
|
-
return;
|
|
19
|
-
let raster = rasters.current.get(page.id);
|
|
20
|
-
if (raster === undefined) {
|
|
21
|
-
raster = await PsScanPipelineService.measure(page.source);
|
|
22
|
-
rasters.current.set(page.id, raster);
|
|
23
|
-
}
|
|
24
|
-
const processed = PsScanPipelineService.process(page, raster, PsScanConfig.previewMaxDimension);
|
|
25
|
-
const blob = await PsScanCanvasService.encode(processed, PsScanConfig.defaultJpegQuality);
|
|
26
|
-
if (isCancelled)
|
|
27
|
-
return;
|
|
28
|
-
const previous = urls.current.get(page.id);
|
|
29
|
-
if (previous !== undefined)
|
|
30
|
-
stale.push(previous);
|
|
31
|
-
urls.current.set(page.id, URL.createObjectURL(blob));
|
|
32
|
-
}
|
|
33
|
-
if (isCancelled)
|
|
34
|
-
return;
|
|
35
|
-
const ids = new Set(pages.map((page) => page.id));
|
|
36
|
-
urls.current.forEach((url, id) => {
|
|
37
|
-
if (ids.has(id))
|
|
38
|
-
return;
|
|
39
|
-
stale.push(url);
|
|
40
|
-
urls.current.delete(id);
|
|
41
|
-
rasters.current.delete(id);
|
|
42
|
-
});
|
|
43
|
-
setPreviews(Object.fromEntries(urls.current));
|
|
44
|
-
setTimeout(() => stale.forEach((url) => URL.revokeObjectURL(url)), 1000);
|
|
45
|
-
}, PsScanConfig.previewDebounceMs);
|
|
46
|
-
return () => {
|
|
47
|
-
isCancelled = true;
|
|
48
|
-
clearTimeout(timer);
|
|
49
|
-
};
|
|
50
|
-
}, [signature]);
|
|
51
|
-
useEffect(() => () => {
|
|
52
|
-
urls.current.forEach((url) => URL.revokeObjectURL(url));
|
|
53
|
-
urls.current.clear();
|
|
54
|
-
rasters.current.clear();
|
|
55
|
-
}, []);
|
|
56
|
-
return previews;
|
|
57
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { PsScanConfig } from '../constants/ps-scan-config';
|
|
2
|
-
import { PsScanFilter } from '../constants/ps-scan-filter';
|
|
3
|
-
import { PsScanImageFilterService } from './ps-scan-image-filter-service';
|
|
4
|
-
import { PsScanRasterService } from './ps-scan-raster-service';
|
|
5
|
-
export class PsScanEnhanceService {
|
|
6
|
-
static apply(source, filter) {
|
|
7
|
-
if (filter === PsScanFilter.Original)
|
|
8
|
-
return source;
|
|
9
|
-
if (filter === PsScanFilter.Colour)
|
|
10
|
-
return PsScanEnhanceService.colour(source);
|
|
11
|
-
if (filter === PsScanFilter.Greyscale)
|
|
12
|
-
return PsScanEnhanceService.greyscale(source);
|
|
13
|
-
return PsScanEnhanceService.document(source);
|
|
14
|
-
}
|
|
15
|
-
static colour(source) {
|
|
16
|
-
const gray = PsScanImageFilterService.grayscale(source);
|
|
17
|
-
const { black, white } = PsScanEnhanceService.levels(gray);
|
|
18
|
-
const gain = 255 / Math.max(1, white - black);
|
|
19
|
-
const target = PsScanRasterService.create(source.width, source.height);
|
|
20
|
-
for (let at = 0; at < source.data.length; at += 4) {
|
|
21
|
-
target.data[at] = (source.data[at] - black) * gain;
|
|
22
|
-
target.data[at + 1] = (source.data[at + 1] - black) * gain;
|
|
23
|
-
target.data[at + 2] = (source.data[at + 2] - black) * gain;
|
|
24
|
-
target.data[at + 3] = 255;
|
|
25
|
-
}
|
|
26
|
-
return target;
|
|
27
|
-
}
|
|
28
|
-
static document(source) {
|
|
29
|
-
const { height, width } = source;
|
|
30
|
-
const gray = PsScanImageFilterService.grayscale(source);
|
|
31
|
-
const integral = new Float64Array((width + 1) * (height + 1));
|
|
32
|
-
for (let y = 0; y < height; y += 1) {
|
|
33
|
-
let row = 0;
|
|
34
|
-
for (let x = 0; x < width; x += 1) {
|
|
35
|
-
row += gray[y * width + x];
|
|
36
|
-
integral[(y + 1) * (width + 1) + x + 1] = integral[y * (width + 1) + x + 1] + row;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const half = Math.max(PsScanConfig.adaptiveWindowMin, Math.floor(Math.min(width, height) / PsScanConfig.adaptiveWindowDivisor)) >> 1;
|
|
40
|
-
const target = PsScanRasterService.create(width, height);
|
|
41
|
-
for (let y = 0; y < height; y += 1) {
|
|
42
|
-
const y0 = Math.max(0, y - half);
|
|
43
|
-
const y1 = Math.min(height, y + half + 1);
|
|
44
|
-
for (let x = 0; x < width; x += 1) {
|
|
45
|
-
const x0 = Math.max(0, x - half);
|
|
46
|
-
const x1 = Math.min(width, x + half + 1);
|
|
47
|
-
const sum = integral[y1 * (width + 1) + x1] -
|
|
48
|
-
integral[y0 * (width + 1) + x1] -
|
|
49
|
-
integral[y1 * (width + 1) + x0] +
|
|
50
|
-
integral[y0 * (width + 1) + x0];
|
|
51
|
-
const mean = sum / ((x1 - x0) * (y1 - y0));
|
|
52
|
-
const value = gray[y * width + x] < mean * PsScanConfig.adaptiveRatio ? 0 : 255;
|
|
53
|
-
const at = (y * width + x) * 4;
|
|
54
|
-
target.data[at] = value;
|
|
55
|
-
target.data[at + 1] = value;
|
|
56
|
-
target.data[at + 2] = value;
|
|
57
|
-
target.data[at + 3] = 255;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return target;
|
|
61
|
-
}
|
|
62
|
-
static greyscale(source) {
|
|
63
|
-
const gray = PsScanImageFilterService.grayscale(source);
|
|
64
|
-
const { black, white } = PsScanEnhanceService.levels(gray);
|
|
65
|
-
const gain = 255 / Math.max(1, white - black);
|
|
66
|
-
const target = PsScanRasterService.create(source.width, source.height);
|
|
67
|
-
for (let index = 0; index < gray.length; index += 1) {
|
|
68
|
-
const value = (gray[index] - black) * gain;
|
|
69
|
-
const at = index * 4;
|
|
70
|
-
target.data[at] = value;
|
|
71
|
-
target.data[at + 1] = value;
|
|
72
|
-
target.data[at + 2] = value;
|
|
73
|
-
target.data[at + 3] = 255;
|
|
74
|
-
}
|
|
75
|
-
return target;
|
|
76
|
-
}
|
|
77
|
-
static levels(gray) {
|
|
78
|
-
const bins = new Uint32Array(256);
|
|
79
|
-
gray.forEach((value) => {
|
|
80
|
-
bins[value] += 1;
|
|
81
|
-
});
|
|
82
|
-
const find = (fraction) => {
|
|
83
|
-
const target = gray.length * fraction;
|
|
84
|
-
let cumulative = 0;
|
|
85
|
-
for (let bin = 0; bin < 256; bin += 1) {
|
|
86
|
-
cumulative += bins[bin];
|
|
87
|
-
if (cumulative >= target)
|
|
88
|
-
return bin;
|
|
89
|
-
}
|
|
90
|
-
return 255;
|
|
91
|
-
};
|
|
92
|
-
return {
|
|
93
|
-
black: find(PsScanConfig.levelLowPercentile),
|
|
94
|
-
white: find(PsScanConfig.levelHighPercentile),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export class PsScanOrderService {
|
|
2
|
-
static move(items, from, to) {
|
|
3
|
-
if (from === to || from < 0 || to < 0 || from >= items.length || to >= items.length)
|
|
4
|
-
return items;
|
|
5
|
-
const next = [...items];
|
|
6
|
-
const [item] = next.splice(from, 1);
|
|
7
|
-
next.splice(to, 0, item);
|
|
8
|
-
return next;
|
|
9
|
-
}
|
|
10
|
-
static step(items, index, direction) {
|
|
11
|
-
return PsScanOrderService.move(items, index, index + direction);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { PsScanOutputFormat } from '../constants/ps-scan-output-format';
|
|
2
|
-
import { PsScanFileNameService } from './ps-scan-file-name-service';
|
|
3
|
-
import { PsScanPdfService } from './ps-scan-pdf-service';
|
|
4
|
-
import { PsScanPipelineService } from './ps-scan-pipeline-service';
|
|
5
|
-
export class PsScanOutputService {
|
|
6
|
-
static async build(pages, options) {
|
|
7
|
-
const rendered = [];
|
|
8
|
-
for (const page of pages)
|
|
9
|
-
rendered.push(await PsScanPipelineService.render(page, options.maxDimension, options.jpegQuality));
|
|
10
|
-
const type = PsScanFileNameService.mimeType(options.format);
|
|
11
|
-
if (options.format === PsScanOutputFormat.Jpeg)
|
|
12
|
-
return rendered.map((item, index) => new File([item.blob], PsScanFileNameService.build(options.fileName, options.format, index, rendered.length), { type }));
|
|
13
|
-
const jpegs = await Promise.all(rendered.map(async (item) => ({
|
|
14
|
-
height: item.height,
|
|
15
|
-
jpeg: new Uint8Array(await item.blob.arrayBuffer()),
|
|
16
|
-
width: item.width,
|
|
17
|
-
})));
|
|
18
|
-
return [
|
|
19
|
-
new File([PsScanPdfService.build(jpegs)], PsScanFileNameService.build(options.fileName, options.format, 0, 1), {
|
|
20
|
-
type,
|
|
21
|
-
}),
|
|
22
|
-
];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { PsScanConfig } from '../constants/ps-scan-config';
|
|
2
|
-
export class PsScanPdfService {
|
|
3
|
-
static build(pages) {
|
|
4
|
-
const encoder = new TextEncoder();
|
|
5
|
-
const chunks = [];
|
|
6
|
-
const offsets = [];
|
|
7
|
-
let length = 0;
|
|
8
|
-
const push = (chunk) => {
|
|
9
|
-
chunks.push(chunk);
|
|
10
|
-
length += chunk.length;
|
|
11
|
-
};
|
|
12
|
-
const text = (value) => push(encoder.encode(value));
|
|
13
|
-
const begin = (id) => {
|
|
14
|
-
offsets[id] = length;
|
|
15
|
-
text(`${id} 0 obj\n`);
|
|
16
|
-
};
|
|
17
|
-
const kids = pages.map((_, index) => `${3 + index * 3} 0 R`).join(' ');
|
|
18
|
-
push(Uint8Array.from([
|
|
19
|
-
0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e, 0x34, 0x0a, 0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a,
|
|
20
|
-
]));
|
|
21
|
-
begin(1);
|
|
22
|
-
text('<< /Type /Catalog /Pages 2 0 R >>\nendobj\n');
|
|
23
|
-
begin(2);
|
|
24
|
-
text(`<< /Type /Pages /Kids [${kids}] /Count ${pages.length} >>\nendobj\n`);
|
|
25
|
-
pages.forEach((page, index) => {
|
|
26
|
-
const pageId = 3 + index * 3;
|
|
27
|
-
const widthPt = PsScanPdfService.points(page.width);
|
|
28
|
-
const heightPt = PsScanPdfService.points(page.height);
|
|
29
|
-
const content = `q ${widthPt} 0 0 ${heightPt} 0 0 cm /Im0 Do Q`;
|
|
30
|
-
begin(pageId);
|
|
31
|
-
text(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${widthPt} ${heightPt}] /Resources << /XObject << /Im0 ${pageId + 2} 0 R >> >> /Contents ${pageId + 1} 0 R >>\nendobj\n`);
|
|
32
|
-
begin(pageId + 1);
|
|
33
|
-
text(`<< /Length ${content.length} >>\nstream\n${content}\nendstream\nendobj\n`);
|
|
34
|
-
begin(pageId + 2);
|
|
35
|
-
text(`<< /Type /XObject /Subtype /Image /Width ${page.width} /Height ${page.height} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ${page.jpeg.length} >>\nstream\n`);
|
|
36
|
-
push(page.jpeg);
|
|
37
|
-
text('\nendstream\nendobj\n');
|
|
38
|
-
});
|
|
39
|
-
const count = 3 + pages.length * 3;
|
|
40
|
-
const xrefAt = length;
|
|
41
|
-
text(`xref\n0 ${count}\n0000000000 65535 f \n`);
|
|
42
|
-
for (let id = 1; id < count; id += 1)
|
|
43
|
-
text(`${String(offsets[id]).padStart(10, '0')} 00000 n \n`);
|
|
44
|
-
text(`trailer\n<< /Size ${count} /Root 1 0 R >>\nstartxref\n${xrefAt}\n%%EOF\n`);
|
|
45
|
-
const out = new Uint8Array(length);
|
|
46
|
-
let at = 0;
|
|
47
|
-
chunks.forEach((chunk) => {
|
|
48
|
-
out.set(chunk, at);
|
|
49
|
-
at += chunk.length;
|
|
50
|
-
});
|
|
51
|
-
return out;
|
|
52
|
-
}
|
|
53
|
-
static points(pixels) {
|
|
54
|
-
return Math.round((pixels * PsScanConfig.pdfPointsPerInch * 100) / PsScanConfig.pdfDpi) / 100;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
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;
|
|
@@ -1,61 +0,0 @@
|
|
|
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;
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
}
|