@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.
Files changed (194) hide show
  1. package/dist/components/ps-document-scanner-loader.d.ts +1 -1
  2. package/dist/components/ps-document-scanner-sheet.d.ts +1 -1
  3. package/dist/components/ps-document-scanner-sheet.js +64 -86
  4. package/dist/components/ps-document-scanner.d.ts +1 -1
  5. package/dist/components/ps-document-scanner.js +2 -3
  6. package/dist/components/ps-file-picker-actions.d.ts +10 -0
  7. package/dist/components/ps-file-picker-actions.js +21 -0
  8. package/dist/components/ps-file-picker.d.ts +7 -1
  9. package/dist/components/ps-file-picker.js +30 -6
  10. package/dist/components/ps-scan-button.d.ts +13 -0
  11. package/dist/components/ps-scan-button.js +9 -0
  12. package/dist/components/ps-scan-camera-view.d.ts +11 -8
  13. package/dist/components/ps-scan-camera-view.js +14 -16
  14. package/dist/components/ps-scan-crop-editor.d.ts +4 -4
  15. package/dist/components/ps-scan-crop-editor.js +24 -8
  16. package/dist/components/ps-scan-denied-steps.d.ts +6 -0
  17. package/dist/components/ps-scan-denied-steps.js +33 -0
  18. package/dist/components/ps-scan-icon-button.d.ts +4 -3
  19. package/dist/components/ps-scan-icon-button.js +3 -1
  20. package/dist/components/ps-scan-illustration.d.ts +1 -0
  21. package/dist/components/ps-scan-illustration.js +6 -0
  22. package/dist/components/ps-scan-loupe.d.ts +10 -0
  23. package/dist/components/ps-scan-loupe.js +31 -0
  24. package/dist/components/ps-scan-permission-view.d.ts +10 -0
  25. package/dist/components/ps-scan-permission-view.js +48 -0
  26. package/dist/components/ps-scan-photo-button.d.ts +7 -0
  27. package/dist/components/ps-scan-photo-button.js +20 -0
  28. package/dist/components/ps-scan-preview.d.ts +12 -0
  29. package/dist/components/ps-scan-preview.js +10 -0
  30. package/dist/components/ps-scan-shutter.d.ts +6 -0
  31. package/dist/components/ps-scan-shutter.js +6 -0
  32. package/dist/components/ps-scan-viewfinder-overlay.d.ts +8 -0
  33. package/dist/components/ps-scan-viewfinder-overlay.js +28 -0
  34. package/dist/components/ps-upload-row.js +1 -1
  35. package/dist/constants/ps-document-scanner-labels.d.ts +39 -38
  36. package/dist/constants/ps-document-scanner-labels.js +39 -38
  37. package/dist/constants/ps-file-picker-labels.d.ts +5 -3
  38. package/dist/constants/ps-file-picker-labels.js +5 -3
  39. package/dist/constants/ps-scan-camera-status.d.ts +4 -0
  40. package/dist/constants/ps-scan-camera-status.js +4 -0
  41. package/dist/constants/ps-scan-chrome-classes.d.ts +10 -0
  42. package/dist/constants/ps-scan-chrome-classes.js +13 -0
  43. package/dist/constants/ps-scan-config.d.ts +34 -24
  44. package/dist/constants/ps-scan-config.js +34 -24
  45. package/dist/constants/ps-scan-platform.d.ts +6 -0
  46. package/dist/constants/ps-scan-platform.js +8 -0
  47. package/dist/constants/ps-scan-step.d.ts +1 -1
  48. package/dist/constants/ps-scan-step.js +1 -1
  49. package/dist/esm/components/ps-document-scanner-sheet.js +67 -89
  50. package/dist/esm/components/ps-document-scanner.js +2 -3
  51. package/dist/esm/components/ps-file-picker-actions.js +17 -0
  52. package/dist/esm/components/ps-file-picker.js +31 -7
  53. package/dist/esm/components/ps-scan-button.js +5 -0
  54. package/dist/esm/components/ps-scan-camera-view.js +17 -19
  55. package/dist/esm/components/ps-scan-crop-editor.js +26 -10
  56. package/dist/esm/components/ps-scan-denied-steps.js +29 -0
  57. package/dist/esm/components/ps-scan-icon-button.js +3 -1
  58. package/dist/esm/components/ps-scan-illustration.js +2 -0
  59. package/dist/esm/components/ps-scan-loupe.js +27 -0
  60. package/dist/esm/components/ps-scan-permission-view.js +44 -0
  61. package/dist/esm/components/ps-scan-photo-button.js +16 -0
  62. package/dist/esm/components/ps-scan-preview.js +6 -0
  63. package/dist/esm/components/ps-scan-shutter.js +2 -0
  64. package/dist/esm/components/ps-scan-viewfinder-overlay.js +24 -0
  65. package/dist/esm/components/ps-upload-row.js +1 -1
  66. package/dist/esm/constants/ps-document-scanner-labels.js +39 -38
  67. package/dist/esm/constants/ps-file-picker-labels.js +5 -3
  68. package/dist/esm/constants/ps-scan-camera-status.js +4 -0
  69. package/dist/esm/constants/ps-scan-chrome-classes.js +10 -0
  70. package/dist/esm/constants/ps-scan-config.js +34 -24
  71. package/dist/esm/constants/ps-scan-platform.js +5 -0
  72. package/dist/esm/constants/ps-scan-step.js +1 -1
  73. package/dist/esm/hooks/use-ps-scan-camera.js +52 -15
  74. package/dist/esm/hooks/use-ps-scan-live-detection.js +41 -0
  75. package/dist/esm/hooks/use-ps-scan-photo.js +42 -0
  76. package/dist/esm/index.js +1 -2
  77. package/dist/esm/models/ps-scan-region.js +1 -0
  78. package/dist/esm/models/ps-scan-result.js +1 -0
  79. package/dist/esm/models/ps-scan-stability.js +1 -0
  80. package/dist/esm/services/ps-scan-camera-service.js +52 -7
  81. package/dist/esm/services/ps-scan-canny-service.js +93 -0
  82. package/dist/esm/services/ps-scan-canvas-service.js +13 -1
  83. package/dist/esm/services/ps-scan-edge-service.js +73 -137
  84. package/dist/esm/services/ps-scan-file-name-service.js +2 -11
  85. package/dist/esm/services/ps-scan-geometry-service.js +56 -0
  86. package/dist/esm/services/ps-scan-hull-service.js +0 -39
  87. package/dist/esm/services/ps-scan-image-filter-service.js +0 -21
  88. package/dist/esm/services/ps-scan-morphology-service.js +44 -0
  89. package/dist/esm/services/ps-scan-pipeline-service.js +9 -19
  90. package/dist/esm/services/ps-scan-platform-service.js +15 -0
  91. package/dist/esm/services/ps-scan-polygon-service.js +75 -0
  92. package/dist/esm/services/ps-scan-region-service.js +52 -0
  93. package/dist/esm/services/ps-scan-stability-service.js +28 -0
  94. package/dist/esm/services/ps-scan-warp-service.js +8 -2
  95. package/dist/esm/services/ps-scan-yield-service.js +5 -0
  96. package/dist/hooks/use-ps-scan-camera.d.ts +3 -1
  97. package/dist/hooks/use-ps-scan-camera.js +52 -15
  98. package/dist/hooks/use-ps-scan-live-detection.d.ts +3 -0
  99. package/dist/hooks/use-ps-scan-live-detection.js +45 -0
  100. package/dist/hooks/use-ps-scan-photo.d.ts +9 -0
  101. package/dist/hooks/use-ps-scan-photo.js +46 -0
  102. package/dist/index.d.ts +1 -2
  103. package/dist/index.js +1 -2
  104. package/dist/models/ps-document-scanner-props.d.ts +0 -3
  105. package/dist/models/ps-document-scanner-sheet-props.d.ts +0 -3
  106. package/dist/models/ps-file-picker-scan.d.ts +4 -0
  107. package/dist/models/ps-scan-gradient.d.ts +4 -0
  108. package/dist/models/ps-scan-live-detection.d.ts +4 -0
  109. package/dist/models/ps-scan-located.d.ts +5 -0
  110. package/dist/models/ps-scan-photo.d.ts +9 -0
  111. package/dist/models/ps-scan-region.d.ts +5 -0
  112. package/dist/models/ps-scan-region.js +2 -0
  113. package/dist/models/ps-scan-result.d.ts +6 -0
  114. package/dist/models/ps-scan-result.js +2 -0
  115. package/dist/models/ps-scan-stability.d.ts +5 -0
  116. package/dist/models/ps-scan-stability.js +2 -0
  117. package/dist/services/ps-scan-camera-service.d.ts +4 -0
  118. package/dist/services/ps-scan-camera-service.js +52 -7
  119. package/dist/services/ps-scan-canny-service.d.ts +7 -0
  120. package/dist/services/ps-scan-canny-service.js +97 -0
  121. package/dist/services/ps-scan-canvas-service.d.ts +1 -0
  122. package/dist/services/ps-scan-canvas-service.js +13 -1
  123. package/dist/services/ps-scan-edge-service.d.ts +5 -5
  124. package/dist/services/ps-scan-edge-service.js +73 -137
  125. package/dist/services/ps-scan-file-name-service.d.ts +1 -3
  126. package/dist/services/ps-scan-file-name-service.js +2 -11
  127. package/dist/services/ps-scan-geometry-service.d.ts +4 -0
  128. package/dist/services/ps-scan-geometry-service.js +56 -0
  129. package/dist/services/ps-scan-hull-service.d.ts +0 -2
  130. package/dist/services/ps-scan-hull-service.js +0 -39
  131. package/dist/services/ps-scan-image-filter-service.d.ts +0 -1
  132. package/dist/services/ps-scan-image-filter-service.js +0 -21
  133. package/dist/services/ps-scan-morphology-service.d.ts +7 -0
  134. package/dist/services/ps-scan-morphology-service.js +48 -0
  135. package/dist/services/ps-scan-pipeline-service.d.ts +2 -5
  136. package/dist/services/ps-scan-pipeline-service.js +9 -19
  137. package/dist/services/ps-scan-platform-service.d.ts +4 -0
  138. package/dist/services/ps-scan-platform-service.js +19 -0
  139. package/dist/services/ps-scan-polygon-service.d.ts +8 -0
  140. package/dist/services/ps-scan-polygon-service.js +79 -0
  141. package/dist/services/ps-scan-region-service.d.ts +4 -0
  142. package/dist/services/ps-scan-region-service.js +56 -0
  143. package/dist/services/ps-scan-stability-service.d.ts +7 -0
  144. package/dist/services/ps-scan-stability-service.js +32 -0
  145. package/dist/services/ps-scan-warp-service.d.ts +1 -1
  146. package/dist/services/ps-scan-warp-service.js +8 -2
  147. package/dist/services/ps-scan-yield-service.d.ts +3 -0
  148. package/dist/services/ps-scan-yield-service.js +9 -0
  149. package/package.json +3 -3
  150. package/dist/components/ps-scan-page-list.d.ts +0 -12
  151. package/dist/components/ps-scan-page-list.js +0 -23
  152. package/dist/components/ps-scan-review.d.ts +0 -23
  153. package/dist/components/ps-scan-review.js +0 -20
  154. package/dist/constants/ps-scan-filter.d.ts +0 -7
  155. package/dist/constants/ps-scan-filter.js +0 -9
  156. package/dist/constants/ps-scan-output-format.d.ts +0 -5
  157. package/dist/constants/ps-scan-output-format.js +0 -7
  158. package/dist/esm/components/ps-scan-page-list.js +0 -19
  159. package/dist/esm/components/ps-scan-review.js +0 -16
  160. package/dist/esm/constants/ps-scan-filter.js +0 -6
  161. package/dist/esm/constants/ps-scan-output-format.js +0 -4
  162. package/dist/esm/hooks/use-ps-scan-pages.js +0 -62
  163. package/dist/esm/hooks/use-ps-scan-previews.js +0 -57
  164. package/dist/esm/services/ps-scan-enhance-service.js +0 -97
  165. package/dist/esm/services/ps-scan-order-service.js +0 -13
  166. package/dist/esm/services/ps-scan-output-service.js +0 -24
  167. package/dist/esm/services/ps-scan-pdf-service.js +0 -56
  168. package/dist/hooks/use-ps-scan-pages.d.ts +0 -12
  169. package/dist/hooks/use-ps-scan-pages.js +0 -66
  170. package/dist/hooks/use-ps-scan-previews.d.ts +0 -2
  171. package/dist/hooks/use-ps-scan-previews.js +0 -61
  172. package/dist/models/ps-scan-added-page.d.ts +0 -5
  173. package/dist/models/ps-scan-levels.d.ts +0 -4
  174. package/dist/models/ps-scan-output-options.d.ts +0 -7
  175. package/dist/models/ps-scan-page.d.ts +0 -12
  176. package/dist/models/ps-scan-pdf-page.d.ts +0 -5
  177. package/dist/services/ps-scan-enhance-service.d.ts +0 -9
  178. package/dist/services/ps-scan-enhance-service.js +0 -101
  179. package/dist/services/ps-scan-order-service.d.ts +0 -4
  180. package/dist/services/ps-scan-order-service.js +0 -17
  181. package/dist/services/ps-scan-output-service.d.ts +0 -5
  182. package/dist/services/ps-scan-output-service.js +0 -28
  183. package/dist/services/ps-scan-pdf-service.d.ts +0 -5
  184. package/dist/services/ps-scan-pdf-service.js +0 -60
  185. /package/dist/esm/models/{ps-scan-added-page.js → ps-file-picker-scan.js} +0 -0
  186. /package/dist/esm/models/{ps-scan-levels.js → ps-scan-gradient.js} +0 -0
  187. /package/dist/esm/models/{ps-scan-output-options.js → ps-scan-live-detection.js} +0 -0
  188. /package/dist/esm/models/{ps-scan-page.js → ps-scan-located.js} +0 -0
  189. /package/dist/esm/models/{ps-scan-pdf-page.js → ps-scan-photo.js} +0 -0
  190. /package/dist/models/{ps-scan-added-page.js → ps-file-picker-scan.js} +0 -0
  191. /package/dist/models/{ps-scan-levels.js → ps-scan-gradient.js} +0 -0
  192. /package/dist/models/{ps-scan-output-options.js → ps-scan-live-detection.js} +0 -0
  193. /package/dist/models/{ps-scan-page.js → ps-scan-located.js} +0 -0
  194. /package/dist/models/{ps-scan-pdf-page.js → ps-scan-photo.js} +0 -0
@@ -3,6 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsScanGeometryService = void 0;
4
4
  const ps_scan_config_1 = require("../constants/ps-scan-config");
5
5
  class PsScanGeometryService {
6
+ static angles(quad) {
7
+ return quad.map((point, index) => {
8
+ const before = quad[(index + quad.length - 1) % quad.length];
9
+ const after = quad[(index + 1) % quad.length];
10
+ const ax = before.x - point.x;
11
+ const ay = before.y - point.y;
12
+ const bx = after.x - point.x;
13
+ const by = after.y - point.y;
14
+ const cosine = (ax * bx + ay * by) / (Math.hypot(ax, ay) * Math.hypot(bx, by));
15
+ return (Math.acos(Math.min(1, Math.max(-1, cosine))) * 180) / Math.PI;
16
+ });
17
+ }
6
18
  static area(points) {
7
19
  let sum = 0;
8
20
  points.forEach((point, index) => {
@@ -14,6 +26,12 @@ class PsScanGeometryService {
14
26
  static clamp(point) {
15
27
  return { x: Math.min(1, Math.max(0, point.x)), y: Math.min(1, Math.max(0, point.y)) };
16
28
  }
29
+ static cover(container, content) {
30
+ if (container.width <= 0 || container.height <= 0 || content.width <= 0 || content.height <= 0)
31
+ return { height: 0, width: 0 };
32
+ const scale = Math.max(container.width / content.width, container.height / content.height);
33
+ return { height: content.height * scale, width: content.width * scale };
34
+ }
17
35
  static defaultQuad() {
18
36
  const inset = 0.08;
19
37
  return [
@@ -26,6 +44,30 @@ class PsScanGeometryService {
26
44
  static distance(from, to) {
27
45
  return Math.hypot(to.x - from.x, to.y - from.y);
28
46
  }
47
+ static expand(quad, distance) {
48
+ const lines = quad.map((point, index) => {
49
+ const next = quad[(index + 1) % quad.length];
50
+ const length = PsScanGeometryService.distance(point, next);
51
+ const nx = (next.y - point.y) / length;
52
+ const ny = -(next.x - point.x) / length;
53
+ return {
54
+ dx: next.x - point.x,
55
+ dy: next.y - point.y,
56
+ x: point.x + nx * distance,
57
+ y: point.y + ny * distance,
58
+ };
59
+ });
60
+ return quad.map((point, index) => {
61
+ const previous = lines[(index + lines.length - 1) % lines.length];
62
+ const current = lines[index];
63
+ const denominator = previous.dx * current.dy - previous.dy * current.dx;
64
+ if (Math.abs(denominator) < 1e-9)
65
+ return point;
66
+ const t = ((current.x - previous.x) * current.dy - (current.y - previous.y) * current.dx) /
67
+ denominator;
68
+ return { x: previous.x + previous.dx * t, y: previous.y + previous.dy * t };
69
+ });
70
+ }
29
71
  static fit(container, content) {
30
72
  if (container.width <= 0 || container.height <= 0 || content.width <= 0 || content.height <= 0)
31
73
  return { height: 0, width: 0 };
@@ -42,6 +84,20 @@ class PsScanGeometryService {
42
84
  width: Math.max(1, Math.round(size.width * scale)),
43
85
  };
44
86
  }
87
+ static inset(quad, distance) {
88
+ const centre = {
89
+ x: quad.reduce((sum, point) => sum + point.x, 0) / quad.length,
90
+ y: quad.reduce((sum, point) => sum + point.y, 0) / quad.length,
91
+ };
92
+ return quad.map((point) => {
93
+ const length = PsScanGeometryService.distance(point, centre);
94
+ const ratio = length <= distance ? 0 : (length - distance) / length;
95
+ return {
96
+ x: centre.x + (point.x - centre.x) * ratio,
97
+ y: centre.y + (point.y - centre.y) * ratio,
98
+ };
99
+ });
100
+ }
45
101
  static isConvex(points) {
46
102
  let sign = 0;
47
103
  for (let index = 0; index < points.length; index += 1) {
@@ -1,6 +1,4 @@
1
1
  import type { PsPoint } from '../models/ps-point';
2
2
  export declare class PsScanHullService {
3
- static bestQuad(hull: PsPoint[]): PsPoint[];
4
3
  static convexHull(input: PsPoint[]): PsPoint[];
5
- private static reduce;
6
4
  }
@@ -1,28 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsScanHullService = 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
4
  class PsScanHullService {
7
- static bestQuad(hull) {
8
- const points = PsScanHullService.reduce(hull, ps_scan_config_1.PsScanConfig.hullCandidateLimit);
9
- if (points.length <= 4)
10
- return points;
11
- let best = points.slice(0, 4);
12
- let bestArea = 0;
13
- for (let a = 0; a < points.length - 3; a += 1)
14
- for (let b = a + 1; b < points.length - 2; b += 1)
15
- for (let c = b + 1; c < points.length - 1; c += 1)
16
- for (let d = c + 1; d < points.length; d += 1) {
17
- const candidate = [points[a], points[b], points[c], points[d]];
18
- const area = ps_scan_geometry_service_1.PsScanGeometryService.area(candidate);
19
- if (area > bestArea) {
20
- bestArea = area;
21
- best = candidate;
22
- }
23
- }
24
- return best;
25
- }
26
5
  static convexHull(input) {
27
6
  const points = [...input].sort((a, b) => (a.x === b.x ? a.y - b.y : a.x - b.x));
28
7
  if (points.length < 3)
@@ -41,23 +20,5 @@ class PsScanHullService {
41
20
  };
42
21
  return [...build(points), ...build([...points].reverse())];
43
22
  }
44
- static reduce(hull, limit) {
45
- const points = [...hull];
46
- while (points.length > limit) {
47
- let weakest = 0;
48
- let weakestArea = Infinity;
49
- points.forEach((point, index) => {
50
- const previous = points[(index - 1 + points.length) % points.length];
51
- const next = points[(index + 1) % points.length];
52
- const area = ps_scan_geometry_service_1.PsScanGeometryService.area([previous, point, next]);
53
- if (area < weakestArea) {
54
- weakestArea = area;
55
- weakest = index;
56
- }
57
- });
58
- points.splice(weakest, 1);
59
- }
60
- return points;
61
- }
62
23
  }
63
24
  exports.PsScanHullService = PsScanHullService;
@@ -2,5 +2,4 @@ import type { PsScanRaster } from '../models/ps-scan-raster';
2
2
  export declare class PsScanImageFilterService {
3
3
  static blur(gray: Uint8ClampedArray, width: number, height: number): Uint8ClampedArray;
4
4
  static grayscale(raster: PsScanRaster): Uint8ClampedArray;
5
- static sobel(gray: Uint8ClampedArray, width: number, height: number): Uint16Array;
6
5
  }
@@ -31,26 +31,5 @@ class PsScanImageFilterService {
31
31
  }
32
32
  return out;
33
33
  }
34
- static sobel(gray, width, height) {
35
- const out = new Uint16Array(width * height);
36
- for (let y = 1; y < height - 1; y += 1)
37
- for (let x = 1; x < width - 1; x += 1) {
38
- const i = y * width + x;
39
- const gx = -gray[i - width - 1] +
40
- gray[i - width + 1] -
41
- 2 * gray[i - 1] +
42
- 2 * gray[i + 1] -
43
- gray[i + width - 1] +
44
- gray[i + width + 1];
45
- const gy = -gray[i - width - 1] -
46
- 2 * gray[i - width] -
47
- gray[i - width + 1] +
48
- gray[i + width - 1] +
49
- 2 * gray[i + width] +
50
- gray[i + width + 1];
51
- out[i] = Math.hypot(gx, gy) / 4;
52
- }
53
- return out;
54
- }
55
34
  }
56
35
  exports.PsScanImageFilterService = PsScanImageFilterService;
@@ -0,0 +1,7 @@
1
+ export declare class PsScanMorphologyService {
2
+ static close(mask: Uint8Array, width: number, height: number, radius: number): Uint8Array;
3
+ static dilate(mask: Uint8Array, width: number, height: number, radius: number): Uint8Array;
4
+ static erode(mask: Uint8Array, width: number, height: number, radius: number): Uint8Array;
5
+ private static horizontal;
6
+ private static vertical;
7
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanMorphologyService = void 0;
4
+ class PsScanMorphologyService {
5
+ static close(mask, width, height, radius) {
6
+ return PsScanMorphologyService.erode(PsScanMorphologyService.dilate(mask, width, height, radius), width, height, radius);
7
+ }
8
+ static dilate(mask, width, height, radius) {
9
+ return PsScanMorphologyService.vertical(PsScanMorphologyService.horizontal(mask, width, height, radius, 1), width, height, radius, 1);
10
+ }
11
+ static erode(mask, width, height, radius) {
12
+ return PsScanMorphologyService.vertical(PsScanMorphologyService.horizontal(mask, width, height, radius, 0), width, height, radius, 0);
13
+ }
14
+ static horizontal(mask, width, height, radius, target) {
15
+ const out = new Uint8Array(mask.length);
16
+ for (let y = 0; y < height; y += 1)
17
+ for (let x = 0; x < width; x += 1) {
18
+ const from = Math.max(0, x - radius);
19
+ const to = Math.min(width - 1, x + radius);
20
+ let hit = target === 1 ? 0 : 1;
21
+ for (let at = from; at <= to; at += 1)
22
+ if (mask[y * width + at] === target) {
23
+ hit = target;
24
+ break;
25
+ }
26
+ out[y * width + x] = hit;
27
+ }
28
+ return out;
29
+ }
30
+ static vertical(mask, width, height, radius, target) {
31
+ const out = new Uint8Array(mask.length);
32
+ for (let y = 0; y < height; y += 1) {
33
+ const from = Math.max(0, y - radius);
34
+ const to = Math.min(height - 1, y + radius);
35
+ for (let x = 0; x < width; x += 1) {
36
+ let hit = target === 1 ? 0 : 1;
37
+ for (let at = from; at <= to; at += 1)
38
+ if (mask[at * width + x] === target) {
39
+ hit = target;
40
+ break;
41
+ }
42
+ out[y * width + x] = hit;
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+ }
48
+ exports.PsScanMorphologyService = PsScanMorphologyService;
@@ -1,10 +1,7 @@
1
- import type { PsScanPage } from '../models/ps-scan-page';
2
1
  import type { PsScanPrepared } from '../models/ps-scan-prepared';
3
- import type { PsScanRaster } from '../models/ps-scan-raster';
2
+ import type { PsScanQuad } from '../models/ps-scan-quad';
4
3
  import type { PsScanRenderedPage } from '../models/ps-scan-rendered-page';
5
4
  export declare class PsScanPipelineService {
6
5
  static prepare(source: Blob): Promise<PsScanPrepared>;
7
- static measure(source: Blob): Promise<PsScanRaster>;
8
- static process(page: Pick<PsScanPage, 'corners' | 'filter' | 'rotation'>, raster: PsScanRaster, maxDimension: number): PsScanRaster;
9
- static render(page: PsScanPage, maxDimension: number, quality: number): Promise<PsScanRenderedPage>;
6
+ static render(source: Blob, corners: PsScanQuad, maxDimension: number, quality: number): Promise<PsScanRenderedPage>;
10
7
  }
@@ -4,36 +4,26 @@ exports.PsScanPipelineService = void 0;
4
4
  const ps_scan_config_1 = require("../constants/ps-scan-config");
5
5
  const ps_scan_canvas_service_1 = require("./ps-scan-canvas-service");
6
6
  const ps_scan_edge_service_1 = require("./ps-scan-edge-service");
7
- const ps_scan_enhance_service_1 = require("./ps-scan-enhance-service");
8
7
  const ps_scan_geometry_service_1 = require("./ps-scan-geometry-service");
9
- const ps_scan_raster_service_1 = require("./ps-scan-raster-service");
10
8
  const ps_scan_warp_service_1 = require("./ps-scan-warp-service");
11
9
  class PsScanPipelineService {
12
10
  static async prepare(source) {
13
11
  const decoded = await ps_scan_canvas_service_1.PsScanCanvasService.decode(source, ps_scan_config_1.PsScanConfig.detectMaxDimension * 2);
14
- const detected = ps_scan_edge_service_1.PsScanEdgeService.detect(decoded.raster);
12
+ const located = await ps_scan_edge_service_1.PsScanEdgeService.locate(decoded.raster, ps_scan_config_1.PsScanConfig.detectMaxDimension);
15
13
  return {
16
- corners: detected === null ? ps_scan_geometry_service_1.PsScanGeometryService.defaultQuad() : detected,
14
+ corners: located.quad,
17
15
  height: decoded.naturalHeight,
18
- isDetected: detected !== null,
16
+ isDetected: located.isDetected,
19
17
  width: decoded.naturalWidth,
20
18
  };
21
19
  }
22
- static async measure(source) {
23
- const decoded = await ps_scan_canvas_service_1.PsScanCanvasService.decode(source, ps_scan_config_1.PsScanConfig.previewMaxDimension);
24
- return decoded.raster;
25
- }
26
- static process(page, raster, maxDimension) {
27
- const size = ps_scan_geometry_service_1.PsScanGeometryService.fitWithin(ps_scan_geometry_service_1.PsScanGeometryService.outputSize(page.corners, raster), maxDimension);
28
- const warped = ps_scan_warp_service_1.PsScanWarpService.warp(raster, ps_scan_geometry_service_1.PsScanGeometryService.toPixels(page.corners, {
29
- height: raster.height - 1,
30
- width: raster.width - 1,
20
+ static async render(source, corners, maxDimension, quality) {
21
+ const decoded = await ps_scan_canvas_service_1.PsScanCanvasService.decode(source, Number.MAX_SAFE_INTEGER);
22
+ const size = ps_scan_geometry_service_1.PsScanGeometryService.fitWithin(ps_scan_geometry_service_1.PsScanGeometryService.outputSize(corners, decoded.raster), maxDimension);
23
+ const raster = await ps_scan_warp_service_1.PsScanWarpService.warp(decoded.raster, ps_scan_geometry_service_1.PsScanGeometryService.toPixels(corners, {
24
+ height: decoded.raster.height - 1,
25
+ width: decoded.raster.width - 1,
31
26
  }), size.width, size.height);
32
- return ps_scan_raster_service_1.PsScanRasterService.rotate(ps_scan_enhance_service_1.PsScanEnhanceService.apply(warped, page.filter), page.rotation);
33
- }
34
- static async render(page, maxDimension, quality) {
35
- const decoded = await ps_scan_canvas_service_1.PsScanCanvasService.decode(page.source, ps_scan_config_1.PsScanConfig.decodeMaxDimension);
36
- const raster = PsScanPipelineService.process(page, decoded.raster, maxDimension);
37
27
  return {
38
28
  blob: await ps_scan_canvas_service_1.PsScanCanvasService.encode(raster, quality),
39
29
  height: raster.height,
@@ -0,0 +1,4 @@
1
+ import { PsScanPlatform } from '../constants/ps-scan-platform';
2
+ export declare class PsScanPlatformService {
3
+ static detect(): PsScanPlatform;
4
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanPlatformService = void 0;
4
+ const ps_scan_platform_1 = require("../constants/ps-scan-platform");
5
+ class PsScanPlatformService {
6
+ static detect() {
7
+ if (typeof navigator === 'undefined')
8
+ return ps_scan_platform_1.PsScanPlatform.Desktop;
9
+ const agent = navigator.userAgent;
10
+ if (/iPhone|iPad|iPod/i.test(agent))
11
+ return ps_scan_platform_1.PsScanPlatform.Ios;
12
+ if (/Macintosh/i.test(agent) && navigator.maxTouchPoints > 1)
13
+ return ps_scan_platform_1.PsScanPlatform.Ios;
14
+ if (/Android/i.test(agent))
15
+ return ps_scan_platform_1.PsScanPlatform.Android;
16
+ return ps_scan_platform_1.PsScanPlatform.Desktop;
17
+ }
18
+ }
19
+ exports.PsScanPlatformService = PsScanPlatformService;
@@ -0,0 +1,8 @@
1
+ import type { PsPoint } from '../models/ps-point';
2
+ export declare class PsScanPolygonService {
3
+ static perimeter(points: PsPoint[]): number;
4
+ static simplify(points: PsPoint[], epsilon: number): PsPoint[];
5
+ static toQuad(hull: PsPoint[]): PsPoint[] | null;
6
+ private static chain;
7
+ private static lineDistance;
8
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanPolygonService = 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
+ class PsScanPolygonService {
7
+ static perimeter(points) {
8
+ return points.reduce((sum, point, index) => sum + ps_scan_geometry_service_1.PsScanGeometryService.distance(point, points[(index + 1) % points.length]), 0);
9
+ }
10
+ static simplify(points, epsilon) {
11
+ if (points.length < 4)
12
+ return points;
13
+ let first = 0;
14
+ let second = 0;
15
+ let best = -1;
16
+ points.forEach((point, index) => {
17
+ const length = ps_scan_geometry_service_1.PsScanGeometryService.distance(points[0], point);
18
+ if (length > best) {
19
+ best = length;
20
+ second = index;
21
+ }
22
+ });
23
+ best = -1;
24
+ points.forEach((point, index) => {
25
+ const length = ps_scan_geometry_service_1.PsScanGeometryService.distance(points[second], point);
26
+ if (length > best) {
27
+ best = length;
28
+ first = index;
29
+ }
30
+ });
31
+ const low = Math.min(first, second);
32
+ const high = Math.max(first, second);
33
+ const forward = points.slice(low, high + 1);
34
+ const backward = [...points.slice(high), ...points.slice(0, low + 1)];
35
+ const left = PsScanPolygonService.chain(forward, epsilon);
36
+ const right = PsScanPolygonService.chain(backward, epsilon);
37
+ return [...left.slice(0, -1), ...right.slice(0, -1)];
38
+ }
39
+ static toQuad(hull) {
40
+ const perimeter = PsScanPolygonService.perimeter(hull);
41
+ for (const ratio of ps_scan_config_1.PsScanConfig.polygonEpsilons) {
42
+ const simple = PsScanPolygonService.simplify(hull, ratio * perimeter);
43
+ if (simple.length === 4)
44
+ return simple;
45
+ if (simple.length < 4)
46
+ return null;
47
+ }
48
+ return null;
49
+ }
50
+ static chain(points, epsilon) {
51
+ if (points.length < 3)
52
+ return points;
53
+ const start = points[0];
54
+ const end = points[points.length - 1];
55
+ let far = 0;
56
+ let farDistance = -1;
57
+ for (let index = 1; index < points.length - 1; index += 1) {
58
+ const length = PsScanPolygonService.lineDistance(points[index], start, end);
59
+ if (length > farDistance) {
60
+ farDistance = length;
61
+ far = index;
62
+ }
63
+ }
64
+ if (farDistance <= epsilon)
65
+ return [start, end];
66
+ const left = PsScanPolygonService.chain(points.slice(0, far + 1), epsilon);
67
+ const right = PsScanPolygonService.chain(points.slice(far), epsilon);
68
+ return [...left.slice(0, -1), ...right];
69
+ }
70
+ static lineDistance(point, from, to) {
71
+ const dx = to.x - from.x;
72
+ const dy = to.y - from.y;
73
+ const length = Math.hypot(dx, dy);
74
+ if (length === 0)
75
+ return ps_scan_geometry_service_1.PsScanGeometryService.distance(point, from);
76
+ return Math.abs(dy * (point.x - from.x) - dx * (point.y - from.y)) / length;
77
+ }
78
+ }
79
+ exports.PsScanPolygonService = PsScanPolygonService;
@@ -0,0 +1,4 @@
1
+ import type { PsScanRegion } from '../models/ps-scan-region';
2
+ export declare class PsScanRegionService {
3
+ static enclosed(mask: Uint8Array, width: number, height: number, minArea: number): PsScanRegion[];
4
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanRegionService = void 0;
4
+ class PsScanRegionService {
5
+ static enclosed(mask, width, height, minArea) {
6
+ const seen = new Uint8Array(mask.length);
7
+ const regions = [];
8
+ for (let start = 0; start < mask.length; start += 1) {
9
+ if (mask[start] === 1 || seen[start] === 1)
10
+ continue;
11
+ const boundary = [];
12
+ const stack = [start];
13
+ let area = 0;
14
+ seen[start] = 1;
15
+ while (stack.length > 0) {
16
+ const index = stack.pop();
17
+ if (index === undefined)
18
+ break;
19
+ const x = index % width;
20
+ const y = (index - x) / width;
21
+ let isBoundary = false;
22
+ area += 1;
23
+ const visit = (next) => {
24
+ if (mask[next] === 1)
25
+ isBoundary = true;
26
+ else if (seen[next] === 0) {
27
+ seen[next] = 1;
28
+ stack.push(next);
29
+ }
30
+ };
31
+ if (x > 0)
32
+ visit(index - 1);
33
+ else
34
+ isBoundary = true;
35
+ if (x < width - 1)
36
+ visit(index + 1);
37
+ else
38
+ isBoundary = true;
39
+ if (y > 0)
40
+ visit(index - width);
41
+ else
42
+ isBoundary = true;
43
+ if (y < height - 1)
44
+ visit(index + width);
45
+ else
46
+ isBoundary = true;
47
+ if (isBoundary)
48
+ boundary.push({ x, y });
49
+ }
50
+ if (area >= minArea)
51
+ regions.push({ area, boundary });
52
+ }
53
+ return regions;
54
+ }
55
+ }
56
+ exports.PsScanRegionService = PsScanRegionService;
@@ -0,0 +1,7 @@
1
+ import type { PsScanQuad } from '../models/ps-scan-quad';
2
+ import type { PsScanStability } from '../models/ps-scan-stability';
3
+ export declare class PsScanStabilityService {
4
+ static initial(): PsScanStability;
5
+ static movement(from: PsScanQuad, to: PsScanQuad): number;
6
+ static next(state: PsScanStability, detected: PsScanQuad | null): PsScanStability;
7
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanStabilityService = 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
+ class PsScanStabilityService {
7
+ static initial() {
8
+ return { misses: 0, quad: null };
9
+ }
10
+ static movement(from, to) {
11
+ return Math.max(...from.map((point, index) => ps_scan_geometry_service_1.PsScanGeometryService.distance(point, to[index])));
12
+ }
13
+ static next(state, detected) {
14
+ if (detected === null) {
15
+ const misses = state.misses + 1;
16
+ return misses > ps_scan_config_1.PsScanConfig.liveMissLimit
17
+ ? { misses, quad: null }
18
+ : { misses, quad: state.quad };
19
+ }
20
+ if (state.quad === null)
21
+ return { misses: 0, quad: detected };
22
+ if (PsScanStabilityService.movement(state.quad, detected) < ps_scan_config_1.PsScanConfig.liveDeadzone)
23
+ return { misses: 0, quad: state.quad };
24
+ const weight = ps_scan_config_1.PsScanConfig.liveSmoothing;
25
+ const quad = state.quad.map((point, index) => ({
26
+ x: point.x + (detected[index].x - point.x) * weight,
27
+ y: point.y + (detected[index].y - point.y) * weight,
28
+ }));
29
+ return { misses: 0, quad };
30
+ }
31
+ }
32
+ exports.PsScanStabilityService = PsScanStabilityService;
@@ -1,6 +1,6 @@
1
1
  import type { PsScanQuad } from '../models/ps-scan-quad';
2
2
  import type { PsScanRaster } from '../models/ps-scan-raster';
3
3
  export declare class PsScanWarpService {
4
- static warp(source: PsScanRaster, pixelQuad: PsScanQuad, width: number, height: number): PsScanRaster;
4
+ static warp(source: PsScanRaster, pixelQuad: PsScanQuad, width: number, height: number): Promise<PsScanRaster>;
5
5
  private static sample;
6
6
  }
@@ -1,19 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsScanWarpService = void 0;
4
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
4
5
  const ps_scan_homography_service_1 = require("./ps-scan-homography-service");
5
6
  const ps_scan_raster_service_1 = require("./ps-scan-raster-service");
7
+ const ps_scan_yield_service_1 = require("./ps-scan-yield-service");
6
8
  class PsScanWarpService {
7
- static warp(source, pixelQuad, width, height) {
9
+ static async warp(source, pixelQuad, width, height) {
8
10
  const matrix = ps_scan_homography_service_1.PsScanHomographyService.fromUnitSquare(pixelQuad);
9
11
  const target = ps_scan_raster_service_1.PsScanRasterService.create(width, height);
10
12
  const spanX = Math.max(1, width - 1);
11
13
  const spanY = Math.max(1, height - 1);
12
- for (let y = 0; y < height; y += 1)
14
+ const rowsPerChunk = Math.max(1, Math.floor(ps_scan_config_1.PsScanConfig.warpChunkPixels / width));
15
+ for (let y = 0; y < height; y += 1) {
13
16
  for (let x = 0; x < width; x += 1) {
14
17
  const point = ps_scan_homography_service_1.PsScanHomographyService.apply(matrix, x / spanX, y / spanY);
15
18
  PsScanWarpService.sample(source, point.x, point.y, target.data, (y * width + x) * 4);
16
19
  }
20
+ if ((y + 1) % rowsPerChunk === 0)
21
+ await ps_scan_yield_service_1.PsScanYieldService.next();
22
+ }
17
23
  return target;
18
24
  }
19
25
  static sample(source, x, y, out, at) {
@@ -0,0 +1,3 @@
1
+ export declare class PsScanYieldService {
2
+ static next(): Promise<void>;
3
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanYieldService = void 0;
4
+ class PsScanYieldService {
5
+ static async next() {
6
+ await new Promise((resolve) => setTimeout(resolve, 0));
7
+ }
8
+ }
9
+ exports.PsScanYieldService = PsScanYieldService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-file-components",
3
- "version": "1.4.0",
3
+ "version": "2.0.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -32,8 +32,8 @@
32
32
  "test": "vitest run"
33
33
  },
34
34
  "dependencies": {
35
- "@pallasoft/ps-core-components": "^2.27.0",
36
- "@pallasoft/ps-icons": "^1.8.1",
35
+ "@pallasoft/ps-core-components": "^2.29.0",
36
+ "@pallasoft/ps-icons": "^1.9.0",
37
37
  "@pallasoft/ps-popovers": "^1.10.2",
38
38
  "@pallasoft/ps-utilities": "^2.19.1",
39
39
  "pdfjs-dist": "^6.3.289"
@@ -1,12 +0,0 @@
1
- import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
2
- import type { PsLabelSet } from '../models/ps-label-set';
3
- import type { PsScanPage } from '../models/ps-scan-page';
4
- export interface PsScanPageListProps {
5
- activeId: string;
6
- labels: PsLabelSet<typeof PsDocumentScannerLabels>;
7
- onMove: (from: number, to: number) => void;
8
- onSelect: (id: string) => void;
9
- pages: PsScanPage[];
10
- previews: Record<string, string>;
11
- }
12
- export declare const PsScanPageList: ({ activeId, labels, onMove, onSelect, pages, previews, }: PsScanPageListProps) => import("react").JSX.Element;
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PsScanPageList = 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_design_system_1 = require("@pallasoft/ps-design-system");
7
- const ps_icons_1 = require("@pallasoft/ps-icons");
8
- const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
9
- const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
10
- const PsScanPageList = ({ activeId, labels, onMove, onSelect, pages, previews, }) => {
11
- const items = pages.map((page, index) => ({
12
- id: page.id,
13
- label: `${labels.page} ${index + 1}`,
14
- }));
15
- const say = (template, label, position) => ps_scan_template_service_1.PsScanTemplateService.fill(template, { count: pages.length, page: label, position });
16
- return ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsSortableList, { announcements: {
17
- cancelled: (label) => say(labels.reorderCancelled, label, 0),
18
- dropped: (label, position) => say(labels.reorderDropped, label, position),
19
- lifted: (label, position) => say(labels.reorderLifted, label, position),
20
- moved: (label, position) => say(labels.reorderMoved, label, position),
21
- }, className: 'flex flex-col gap-2', getHandleLabel: (label) => ps_scan_template_service_1.PsScanTemplateService.fill(labels.dragPage, { page: label }), instructions: labels.reorderInstructions, items: items, onMove: (_id, from, to) => onMove(from, to), renderItem: ({ handle, index, isDragging, item }) => ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.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, (0, jsx_runtime_1.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 ? ((0, jsx_runtime_1.jsx)("span", { className: 'ps-skeleton h-14 w-10 shrink-0 rounded' })) : ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'h-14 w-10 shrink-0 rounded border border-line bg-white object-cover object-top', src: previews[item.id] })), (0, jsx_runtime_1.jsx)("span", { className: 'truncate text-sm font-medium text-ink', children: item.label })] }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { disabled: index === 0, label: ps_scan_template_service_1.PsScanTemplateService.fill(labels.moveUp, { page: item.label }), onClick: () => onMove(index, index - 1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5 -rotate-90', iconKey: ps_icons_1.PsIconKey.ChevronRight }) }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { disabled: index === pages.length - 1, label: ps_scan_template_service_1.PsScanTemplateService.fill(labels.moveDown, { page: item.label }), onClick: () => onMove(index, index + 1), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5 rotate-90', iconKey: ps_icons_1.PsIconKey.ChevronRight }) })] })), role: 'list' }));
22
- };
23
- exports.PsScanPageList = PsScanPageList;