react-native-rectangle-doc-scanner 3.31.0 → 3.33.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.
@@ -92,11 +92,8 @@ const CropEditor = ({ document, overlayColor = 'rgba(0,0,0,0.5)', overlayStrokeC
92
92
  ? document.rectangle
93
93
  : document.quad && document.quad.length === 4
94
94
  ? (0, coordinate_1.quadToRectangle)(document.quad)
95
- : null;
96
- if (!sourceRectangle) {
97
- return undefined;
98
- }
99
- const scaled = (0, coordinate_1.scaleRectangle)(sourceRectangle, baseWidth, baseHeight, imageSize.width, imageSize.height);
95
+ : (0, coordinate_1.createFullImageRectangle)(baseWidth, baseHeight);
96
+ const scaled = (0, coordinate_1.scaleRectangle)(sourceRectangle ?? (0, coordinate_1.createFullImageRectangle)(baseWidth, baseHeight), baseWidth, baseHeight, imageSize.width, imageSize.height);
100
97
  return scaled;
101
98
  }, [document.rectangle, document.quad, document.width, document.height, imageSize]);
102
99
  const handleImageLoad = (0, react_1.useCallback)((event) => {
@@ -140,7 +140,12 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
140
140
  return Promise.reject(error);
141
141
  }
142
142
  if (result && typeof result.then === 'function') {
143
- return result.catch((error) => {
143
+ return result
144
+ .then((payload) => {
145
+ handlePictureTaken(payload);
146
+ return payload;
147
+ })
148
+ .catch((error) => {
144
149
  captureOriginRef.current = 'auto';
145
150
  throw error;
146
151
  });
@@ -157,7 +162,7 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
157
162
  },
158
163
  };
159
164
  });
160
- }, []);
165
+ }, [handlePictureTaken]);
161
166
  const handleManualCapture = (0, react_1.useCallback)(() => {
162
167
  captureOriginRef.current = 'manual';
163
168
  capture().catch((error) => {
@@ -41,12 +41,6 @@ const CropEditor_1 = require("./CropEditor");
41
41
  const coordinate_1 = require("./utils/coordinate");
42
42
  const stripFileUri = (value) => value.replace(/^file:\/\//, '');
43
43
  const ensureFileUri = (value) => (value.startsWith('file://') ? value : `file://${value}`);
44
- const createFullImageRectangle = (width, height) => ({
45
- topLeft: { x: 0, y: 0 },
46
- topRight: { x: width, y: 0 },
47
- bottomRight: { x: width, y: height },
48
- bottomLeft: { x: 0, y: height },
49
- });
50
44
  const resolveImageSize = (path, fallbackWidth, fallbackHeight) => new Promise((resolve) => {
51
45
  react_native_1.Image.getSize(ensureFileUri(path), (width, height) => resolve({ width, height }), () => resolve({
52
46
  width: fallbackWidth > 0 ? fallbackWidth : 0,
@@ -54,9 +48,10 @@ const resolveImageSize = (path, fallbackWidth, fallbackHeight) => new Promise((r
54
48
  }));
55
49
  });
56
50
  const normalizeCapturedDocument = (document) => {
51
+ const { origin: _origin, ...rest } = document;
57
52
  const normalizedPath = stripFileUri(document.initialPath ?? document.path);
58
53
  return {
59
- ...document,
54
+ ...rest,
60
55
  path: normalizedPath,
61
56
  initialPath: document.initialPath ? stripFileUri(document.initialPath) : normalizedPath,
62
57
  croppedPath: document.croppedPath ? stripFileUri(document.croppedPath) : null,
@@ -104,10 +99,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
104
99
  initialRectangle = (0, coordinate_1.scaleRectangle)(quadRectangle, baseWidth, baseHeight, imageSize.width, imageSize.height);
105
100
  }
106
101
  }
107
- if (initialRectangle) {
108
- cropInitializedRef.current = true;
109
- setCropRectangle(initialRectangle);
110
- }
102
+ cropInitializedRef.current = true;
103
+ setCropRectangle(initialRectangle ?? (0, coordinate_1.createFullImageRectangle)(imageSize.width || 1, imageSize.height || 1));
111
104
  }, [capturedDoc, imageSize]);
112
105
  const resetState = (0, react_1.useCallback)(() => {
113
106
  setScreen('scanner');
@@ -150,7 +143,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
150
143
  const scaledRectangle = rectangleBase
151
144
  ? (0, coordinate_1.scaleRectangle)(rectangleBase, baseWidth || targetWidth, baseHeight || targetHeight, targetWidth, targetHeight)
152
145
  : null;
153
- const rectangleToUse = scaledRectangle ?? createFullImageRectangle(targetWidth, targetHeight);
146
+ const rectangleToUse = scaledRectangle ?? (0, coordinate_1.createFullImageRectangle)(targetWidth, targetHeight);
154
147
  const base64 = await new Promise((resolve, reject) => {
155
148
  cropManager.crop({
156
149
  topLeft: rectangleToUse.topLeft,
@@ -247,7 +240,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
247
240
  fallbackRectangle = (0, coordinate_1.scaleRectangle)(quadRectangle, baseWidth || targetWidth, baseHeight || targetHeight, targetWidth, targetHeight);
248
241
  }
249
242
  }
250
- const rectangleToUse = cropRectangle ?? fallbackRectangle ?? createFullImageRectangle(targetWidth, targetHeight);
243
+ const rectangleToUse = cropRectangle ?? fallbackRectangle ?? (0, coordinate_1.createFullImageRectangle)(targetWidth, targetHeight);
251
244
  const base64 = await new Promise((resolve, reject) => {
252
245
  cropManager.crop({
253
246
  topLeft: rectangleToUse.topLeft,
@@ -299,7 +292,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
299
292
  }, [onClose, resetState]);
300
293
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
301
294
  screen === 'scanner' && (react_1.default.createElement(react_native_1.View, { style: styles.flex },
302
- react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: !manualCapture, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture },
295
+ react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: !manualCapture, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, showManualCaptureButton: true },
303
296
  react_1.default.createElement(react_native_1.View, { style: styles.overlay, pointerEvents: "box-none" },
304
297
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.closeButton, onPress: handleClose, accessibilityLabel: mergedStrings.cancel, accessibilityRole: "button" },
305
298
  react_1.default.createElement(react_native_1.Text, { style: styles.closeButtonLabel }, "\u00D7")),
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export { FullDocScanner } from './FullDocScanner';
4
4
  export type { FullDocScannerResult, FullDocScannerProps, FullDocScannerStrings, } from './FullDocScanner';
5
5
  export type { Point, Quad, Rectangle, CapturedDocument } from './types';
6
6
  export type { DetectionConfig, DocScannerHandle, DocScannerCapture, RectangleDetectEvent, } from './DocScanner';
7
- export { quadToRectangle, rectangleToQuad, scaleCoordinates, scaleRectangle, } from './utils/coordinate';
7
+ export { quadToRectangle, rectangleToQuad, scaleCoordinates, scaleRectangle, createFullImageRectangle, } from './utils/coordinate';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scaleRectangle = exports.scaleCoordinates = exports.rectangleToQuad = exports.quadToRectangle = exports.FullDocScanner = exports.CropEditor = exports.DocScanner = void 0;
3
+ exports.createFullImageRectangle = exports.scaleRectangle = exports.scaleCoordinates = exports.rectangleToQuad = exports.quadToRectangle = exports.FullDocScanner = exports.CropEditor = exports.DocScanner = void 0;
4
4
  // Main components
5
5
  var DocScanner_1 = require("./DocScanner");
6
6
  Object.defineProperty(exports, "DocScanner", { enumerable: true, get: function () { return DocScanner_1.DocScanner; } });
@@ -14,3 +14,4 @@ Object.defineProperty(exports, "quadToRectangle", { enumerable: true, get: funct
14
14
  Object.defineProperty(exports, "rectangleToQuad", { enumerable: true, get: function () { return coordinate_1.rectangleToQuad; } });
15
15
  Object.defineProperty(exports, "scaleCoordinates", { enumerable: true, get: function () { return coordinate_1.scaleCoordinates; } });
16
16
  Object.defineProperty(exports, "scaleRectangle", { enumerable: true, get: function () { return coordinate_1.scaleRectangle; } });
17
+ Object.defineProperty(exports, "createFullImageRectangle", { enumerable: true, get: function () { return coordinate_1.createFullImageRectangle; } });
@@ -8,6 +8,7 @@ export declare const quadToRectangle: (quad: Point[]) => Rectangle | null;
8
8
  * Convert Rectangle format back to quad points array
9
9
  */
10
10
  export declare const rectangleToQuad: (rect: Rectangle) => Point[];
11
+ export declare const createFullImageRectangle: (width: number, height: number) => Rectangle;
11
12
  /**
12
13
  * Scale coordinates from one dimension to another
13
14
  * Useful when image dimensions differ from display dimensions
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scaleRectangle = exports.scaleCoordinates = exports.rectangleToQuad = exports.quadToRectangle = void 0;
3
+ exports.scaleRectangle = exports.scaleCoordinates = exports.createFullImageRectangle = exports.rectangleToQuad = exports.quadToRectangle = void 0;
4
4
  /**
5
5
  * Convert quad points array to Rectangle format for perspective cropper
6
6
  * Assumes quad points are ordered: [topLeft, topRight, bottomRight, bottomLeft]
@@ -29,6 +29,13 @@ const rectangleToQuad = (rect) => {
29
29
  ];
30
30
  };
31
31
  exports.rectangleToQuad = rectangleToQuad;
32
+ const createFullImageRectangle = (width, height) => ({
33
+ topLeft: { x: 0, y: 0 },
34
+ topRight: { x: width, y: 0 },
35
+ bottomRight: { x: width, y: height },
36
+ bottomLeft: { x: 0, y: height },
37
+ });
38
+ exports.createFullImageRectangle = createFullImageRectangle;
32
39
  /**
33
40
  * Scale coordinates from one dimension to another
34
41
  * Useful when image dimensions differ from display dimensions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.31.0",
3
+ "version": "3.33.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -3,7 +3,7 @@ import { View, StyleSheet, Image, Dimensions, ActivityIndicator, Text } from 're
3
3
  import CustomImageCropper from 'react-native-perspective-image-cropper';
4
4
  import type { Rectangle as CropperRectangle } from 'react-native-perspective-image-cropper';
5
5
  import type { Point, Rectangle, CapturedDocument } from './types';
6
- import { quadToRectangle, scaleRectangle } from './utils/coordinate';
6
+ import { createFullImageRectangle, quadToRectangle, scaleRectangle } from './utils/coordinate';
7
7
 
8
8
  interface CropEditorProps {
9
9
  document: CapturedDocument;
@@ -81,14 +81,10 @@ export const CropEditor: React.FC<CropEditorProps> = ({
81
81
  ? document.rectangle
82
82
  : document.quad && document.quad.length === 4
83
83
  ? quadToRectangle(document.quad)
84
- : null;
85
-
86
- if (!sourceRectangle) {
87
- return undefined;
88
- }
84
+ : createFullImageRectangle(baseWidth, baseHeight);
89
85
 
90
86
  const scaled = scaleRectangle(
91
- sourceRectangle,
87
+ sourceRectangle ?? createFullImageRectangle(baseWidth, baseHeight),
92
88
  baseWidth,
93
89
  baseHeight,
94
90
  imageSize.width,
@@ -212,10 +212,15 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
212
212
  return Promise.reject(error);
213
213
  }
214
214
  if (result && typeof result.then === 'function') {
215
- return result.catch((error: unknown) => {
216
- captureOriginRef.current = 'auto';
217
- throw error;
218
- });
215
+ return result
216
+ .then((payload: PictureEvent) => {
217
+ handlePictureTaken(payload);
218
+ return payload;
219
+ })
220
+ .catch((error: unknown) => {
221
+ captureOriginRef.current = 'auto';
222
+ throw error;
223
+ });
219
224
  }
220
225
 
221
226
  return new Promise<PictureEvent>((resolve, reject) => {
@@ -230,7 +235,7 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
230
235
  },
231
236
  };
232
237
  });
233
- }, []);
238
+ }, [handlePictureTaken]);
234
239
 
235
240
  const handleManualCapture = useCallback(() => {
236
241
  captureOriginRef.current = 'manual';
@@ -13,7 +13,7 @@ import { DocScanner } from './DocScanner';
13
13
  import { CropEditor } from './CropEditor';
14
14
  import type { CapturedDocument, Point, Quad, Rectangle } from './types';
15
15
  import type { DetectionConfig, DocScannerHandle, DocScannerCapture } from './DocScanner';
16
- import { quadToRectangle, scaleRectangle } from './utils/coordinate';
16
+ import { createFullImageRectangle, quadToRectangle, scaleRectangle } from './utils/coordinate';
17
17
 
18
18
  type CustomCropManagerType = {
19
19
  crop: (
@@ -34,13 +34,6 @@ const stripFileUri = (value: string) => value.replace(/^file:\/\//, '');
34
34
 
35
35
  const ensureFileUri = (value: string) => (value.startsWith('file://') ? value : `file://${value}`);
36
36
 
37
- const createFullImageRectangle = (width: number, height: number): Rectangle => ({
38
- topLeft: { x: 0, y: 0 },
39
- topRight: { x: width, y: 0 },
40
- bottomRight: { x: width, y: height },
41
- bottomLeft: { x: 0, y: height },
42
- });
43
-
44
37
  const resolveImageSize = (
45
38
  path: string,
46
39
  fallbackWidth: number,
@@ -58,9 +51,10 @@ const resolveImageSize = (
58
51
  });
59
52
 
60
53
  const normalizeCapturedDocument = (document: DocScannerCapture): CapturedDocument => {
54
+ const { origin: _origin, ...rest } = document;
61
55
  const normalizedPath = stripFileUri(document.initialPath ?? document.path);
62
56
  return {
63
- ...document,
57
+ ...rest,
64
58
  path: normalizedPath,
65
59
  initialPath: document.initialPath ? stripFileUri(document.initialPath) : normalizedPath,
66
60
  croppedPath: document.croppedPath ? stripFileUri(document.croppedPath) : null,
@@ -184,10 +178,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
184
178
  }
185
179
  }
186
180
 
187
- if (initialRectangle) {
188
- cropInitializedRef.current = true;
189
- setCropRectangle(initialRectangle);
190
- }
181
+ cropInitializedRef.current = true;
182
+ setCropRectangle(
183
+ initialRectangle ?? createFullImageRectangle(imageSize.width || 1, imageSize.height || 1),
184
+ );
191
185
  }, [capturedDoc, imageSize]);
192
186
 
193
187
  const resetState = useCallback(() => {
@@ -462,6 +456,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
462
456
  minStableFrames={minStableFrames ?? 6}
463
457
  detectionConfig={detectionConfig}
464
458
  onCapture={handleCapture}
459
+ showManualCaptureButton
465
460
  >
466
461
  <View style={styles.overlay} pointerEvents="box-none">
467
462
  <TouchableOpacity
package/src/index.ts CHANGED
@@ -24,4 +24,5 @@ export {
24
24
  rectangleToQuad,
25
25
  scaleCoordinates,
26
26
  scaleRectangle,
27
+ createFullImageRectangle,
27
28
  } from './utils/coordinate';
@@ -29,6 +29,13 @@ export const rectangleToQuad = (rect: Rectangle): Point[] => {
29
29
  ];
30
30
  };
31
31
 
32
+ export const createFullImageRectangle = (width: number, height: number): Rectangle => ({
33
+ topLeft: { x: 0, y: 0 },
34
+ topRight: { x: width, y: 0 },
35
+ bottomRight: { x: width, y: height },
36
+ bottomLeft: { x: 0, y: height },
37
+ });
38
+
32
39
  /**
33
40
  * Scale coordinates from one dimension to another
34
41
  * Useful when image dimensions differ from display dimensions