react-native-rectangle-doc-scanner 1.13.0 → 1.15.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/README.md +49 -120
- package/dist/DocScanner.d.ts +6 -8
- package/dist/DocScanner.js +55 -108
- package/package.json +7 -3
- package/patches/react-native-document-scanner+1.8.0.patch +22 -0
- package/src/DocScanner.tsx +121 -241
- package/src/external.d.ts +28 -12
- package/android/build.gradle +0 -55
- package/android/consumer-rules.pro +0 -1
- package/android/proguard-rules.pro +0 -1
- package/android/src/main/java/com/reactnativerectangledocscanner/RNRDocScannerModule.kt +0 -37
- package/android/src/main/java/com/reactnativerectangledocscanner/RNRDocScannerPackage.kt +0 -16
- package/android/src/main/java/com/reactnativerectangledocscanner/RNRDocScannerView.kt +0 -568
- package/android/src/main/java/com/reactnativerectangledocscanner/RNRDocScannerViewManager.kt +0 -50
- package/docs/native-module-architecture.md +0 -178
- package/ios/RNRDocScannerModule.swift +0 -49
- package/ios/RNRDocScannerView.swift +0 -694
- package/ios/RNRDocScannerViewManager.m +0 -22
- package/ios/RNRDocScannerViewManager.swift +0 -47
- package/react-native-rectangle-doc-scanner.podspec +0 -22
- package/src/utils/overlay.tsx +0 -208
- package/src/utils/quad.ts +0 -181
- package/src/utils/stability.ts +0 -32
package/README.md
CHANGED
|
@@ -1,86 +1,52 @@
|
|
|
1
|
-
# React Native
|
|
1
|
+
# React Native Document Scanner Wrapper
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
>
|
|
5
|
-
> A native VisionKit (iOS) + CameraX/ML Kit (Android) implementation is being scaffolded to replace the previous VisionCamera/OpenCV pipeline. The JavaScript API is already aligned with the native contract; the detection/capture engines will be filled in next. See [`docs/native-module-architecture.md`](docs/native-module-architecture.md) for the roadmap.
|
|
3
|
+
React Native-friendly wrapper around [`react-native-document-scanner`](https://github.com/Michaelvilleneuve/react-native-document-scanner). It exposes a declarative `<DocScanner />` component that renders the native document scanner on both iOS and Android while keeping the surface area small enough to plug into custom UIs.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- Native camera preview surfaces on iOS/Android with React overlay support
|
|
12
|
-
- Polygon detection events (with stability counter) delivered every frame
|
|
13
|
-
- Skia-powered outline + optional 3×3 grid overlay
|
|
14
|
-
- Auto-capture and manual capture flows using the same API
|
|
15
|
-
- Optional `CropEditor` powered by `react-native-perspective-image-cropper`
|
|
16
|
-
|
|
17
|
-
## Requirements
|
|
18
|
-
|
|
19
|
-
- React Native 0.70+
|
|
20
|
-
- iOS 13+ (VisionKit availability) / Android 7.0+ (API 24)
|
|
21
|
-
- Camera permission strings in your host app (`NSCameraUsageDescription`, Android runtime permission handling)
|
|
22
|
-
- Peer dependencies:
|
|
23
|
-
- `@shopify/react-native-skia`
|
|
24
|
-
- `react-native-perspective-image-cropper`
|
|
25
|
-
- `react`
|
|
26
|
-
- `react-native`
|
|
5
|
+
> The native implementation lives inside the upstream library (Objective‑C/OpenCV on iOS, Kotlin/OpenCV on Android). This package simply re-exports a type-safe wrapper, optional crop editor helpers, and a full-screen scanner flow.
|
|
27
6
|
|
|
28
7
|
## Installation
|
|
29
8
|
|
|
30
|
-
```
|
|
9
|
+
```bash
|
|
31
10
|
yarn add react-native-rectangle-doc-scanner \
|
|
32
|
-
|
|
11
|
+
github:Michaelvilleneuve/react-native-document-scanner \
|
|
33
12
|
react-native-perspective-image-cropper
|
|
34
13
|
|
|
35
14
|
# iOS
|
|
36
15
|
cd ios && pod install
|
|
37
16
|
```
|
|
38
17
|
|
|
39
|
-
Android
|
|
18
|
+
Android automatically links the native module. If you manage packages manually (legacy architecture), register `DocumentScannerPackage()` in your `MainApplication`.
|
|
40
19
|
|
|
41
20
|
## Usage
|
|
42
21
|
|
|
43
22
|
```tsx
|
|
44
|
-
import React, {
|
|
45
|
-
import { StyleSheet, Text, View } from 'react-native';
|
|
46
|
-
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} from 'react-native-rectangle-doc-scanner';
|
|
51
|
-
|
|
52
|
-
export const ScanScreen: React.FC = () => {
|
|
53
|
-
const [capturedDoc, setCapturedDoc] = useState<CapturedDocument | null>(null);
|
|
54
|
-
|
|
55
|
-
if (capturedDoc) {
|
|
56
|
-
return (
|
|
57
|
-
<CropEditor
|
|
58
|
-
document={capturedDoc}
|
|
59
|
-
overlayColor="rgba(0,0,0,0.6)"
|
|
60
|
-
overlayStrokeColor="#e7a649"
|
|
61
|
-
handlerColor="#e7a649"
|
|
62
|
-
onCropChange={(rectangle) => {
|
|
63
|
-
console.log('Adjusted corners:', rectangle);
|
|
64
|
-
}}
|
|
65
|
-
/>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
23
|
+
import React, { useRef } from 'react';
|
|
24
|
+
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
25
|
+
import { DocScanner, type DocScannerHandle } from 'react-native-rectangle-doc-scanner';
|
|
26
|
+
|
|
27
|
+
export const ScanScreen = () => {
|
|
28
|
+
const scannerRef = useRef<DocScannerHandle>(null);
|
|
68
29
|
|
|
69
30
|
return (
|
|
70
31
|
<View style={styles.container}>
|
|
71
32
|
<DocScanner
|
|
72
|
-
|
|
73
|
-
|
|
33
|
+
ref={scannerRef}
|
|
34
|
+
overlayColor="rgba(0, 126, 244, 0.35)"
|
|
74
35
|
autoCapture
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
36
|
+
minStableFrames={6}
|
|
37
|
+
onCapture={(result) => {
|
|
38
|
+
console.log('Captured document:', result.path);
|
|
78
39
|
}}
|
|
79
40
|
>
|
|
80
|
-
<View style={styles.overlay}>
|
|
81
|
-
<Text style={styles.hint}>Align the document
|
|
41
|
+
<View style={styles.overlay} pointerEvents="none">
|
|
42
|
+
<Text style={styles.hint}>Align the document inside the frame</Text>
|
|
82
43
|
</View>
|
|
83
44
|
</DocScanner>
|
|
45
|
+
|
|
46
|
+
<TouchableOpacity
|
|
47
|
+
style={styles.captureButton}
|
|
48
|
+
onPress={() => scannerRef.current?.capture()}
|
|
49
|
+
/>
|
|
84
50
|
</View>
|
|
85
51
|
);
|
|
86
52
|
};
|
|
@@ -92,78 +58,41 @@ const styles = StyleSheet.create({
|
|
|
92
58
|
top: 60,
|
|
93
59
|
alignSelf: 'center',
|
|
94
60
|
paddingHorizontal: 20,
|
|
95
|
-
paddingVertical:
|
|
61
|
+
paddingVertical: 10,
|
|
96
62
|
borderRadius: 12,
|
|
97
|
-
backgroundColor: 'rgba(0,0,0,0.
|
|
63
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
64
|
+
},
|
|
65
|
+
hint: { color: '#fff', fontWeight: '600' },
|
|
66
|
+
captureButton: {
|
|
67
|
+
position: 'absolute',
|
|
68
|
+
bottom: 40,
|
|
69
|
+
alignSelf: 'center',
|
|
70
|
+
width: 70,
|
|
71
|
+
height: 70,
|
|
72
|
+
borderRadius: 35,
|
|
73
|
+
backgroundColor: '#fff',
|
|
98
74
|
},
|
|
99
|
-
hint: { color: '#fff', fontSize: 15, fontWeight: '600' },
|
|
100
75
|
});
|
|
101
76
|
```
|
|
102
77
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
## API
|
|
106
|
-
|
|
107
|
-
### `<DocScanner />`
|
|
108
|
-
|
|
109
|
-
| Prop | Type | Default | Description |
|
|
110
|
-
| --- | --- | --- | --- |
|
|
111
|
-
| `onCapture` | `(result) => void` | – | Fired when a capture resolves. Returns `path`, `width`, `height`, `quad`. |
|
|
112
|
-
| `overlayColor` | `string` | `#e7a649` | Stroke colour for the overlay outline. |
|
|
113
|
-
| `autoCapture` | `boolean` | `true` | When `true`, capture is triggered automatically once stability is reached. |
|
|
114
|
-
| `minStableFrames` | `number` | `8` | Number of stable frames before auto capture fires. |
|
|
115
|
-
| `enableTorch` | `boolean` | `false` | Toggles device torch (if supported). |
|
|
116
|
-
| `quality` | `number` | `90` | JPEG quality (0–100). |
|
|
117
|
-
| `useBase64` | `boolean` | `false` | Return base64 strings instead of file URIs. |
|
|
118
|
-
| `showGrid` | `boolean` | `true` | Show the 3×3 helper grid inside the overlay. |
|
|
119
|
-
| `gridColor` | `string` | `rgba(231,166,73,0.35)` | Colour of grid lines. |
|
|
120
|
-
| `gridLineWidth` | `number` | `2` | Width of grid lines. |
|
|
121
|
-
|
|
122
|
-
Imperative helpers are exposed via `DocScannerHandle`:
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
import { DocScanner, type DocScannerHandle } from 'react-native-rectangle-doc-scanner';
|
|
126
|
-
|
|
127
|
-
const ref = useRef<DocScannerHandle>(null);
|
|
128
|
-
|
|
129
|
-
const fireCapture = () => {
|
|
130
|
-
ref.current?.capture().then((result) => {
|
|
131
|
-
console.log(result);
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
> The native module currently returns a `"not_implemented"` error from `capture()` until the VisionKit / ML Kit integration is finished. The JS surface is ready for when the native pipeline lands.
|
|
78
|
+
`<DocScanner />` passes through the important upstream props:
|
|
137
79
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
| Prop | Type | Default | Description |
|
|
80
|
+
| Prop | Type | Default | Notes |
|
|
141
81
|
| --- | --- | --- | --- |
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
|
|
149
|
-
## Native scaffolding status
|
|
82
|
+
| `overlayColor` | `string` | `#0b7ef4` | Native overlay tint. |
|
|
83
|
+
| `autoCapture` | `boolean` | `true` | Maps to `manualOnly` internally. |
|
|
84
|
+
| `minStableFrames` | `number` | `8` | Detection count before auto capture. |
|
|
85
|
+
| `enableTorch` | `boolean` | `false` | Toggle device torch. |
|
|
86
|
+
| `quality` | `number` | `90` | 0–100 (converted for native). |
|
|
87
|
+
| `useBase64` | `boolean` | `false` | Return base64 payloads instead of file URIs. |
|
|
88
|
+
| `onCapture` | `(result) => void` | — | Receives `{ path, quad: null, width, height }`. |
|
|
150
89
|
|
|
151
|
-
|
|
152
|
-
- ✅ iOS view manager / module skeleton (Swift)
|
|
153
|
-
- ✅ Android view manager / module skeleton (Kotlin)
|
|
154
|
-
- ☐ VisionKit rectangle detection & capture pipeline
|
|
155
|
-
- ☐ CameraX + ML Kit rectangle detection & capture pipeline
|
|
156
|
-
- ☐ Base64 / file output parity tests
|
|
90
|
+
Manual capture exposes an imperative `capture()` method via `ref`. Children render on top of the camera preview so you can build your own buttons, progress indicators, or onboarding tips.
|
|
157
91
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
## Build
|
|
161
|
-
|
|
162
|
-
```sh
|
|
163
|
-
yarn build
|
|
164
|
-
```
|
|
92
|
+
## Convenience APIs
|
|
165
93
|
|
|
166
|
-
|
|
94
|
+
- `CropEditor` – wraps `react-native-perspective-image-cropper` for manual corner adjustment.
|
|
95
|
+
- `FullDocScanner` – puts the scanner and crop editor into a single modal-like flow.
|
|
167
96
|
|
|
168
97
|
## License
|
|
169
98
|
|
package/dist/DocScanner.d.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
|
-
import type { Point } from './types';
|
|
3
2
|
type PictureEvent = {
|
|
4
3
|
croppedImage?: string | null;
|
|
5
|
-
initialImage?: string;
|
|
4
|
+
initialImage?: string | null;
|
|
6
5
|
width?: number;
|
|
7
6
|
height?: number;
|
|
8
7
|
};
|
|
9
|
-
type DocScannerHandle = {
|
|
10
|
-
capture: () => Promise<PictureEvent>;
|
|
11
|
-
reset: () => void;
|
|
12
|
-
};
|
|
13
8
|
export interface DetectionConfig {
|
|
14
9
|
processingWidth?: number;
|
|
15
10
|
cannyLowThreshold?: number;
|
|
@@ -21,7 +16,7 @@ export interface DetectionConfig {
|
|
|
21
16
|
interface Props {
|
|
22
17
|
onCapture?: (photo: {
|
|
23
18
|
path: string;
|
|
24
|
-
quad:
|
|
19
|
+
quad: null;
|
|
25
20
|
width: number;
|
|
26
21
|
height: number;
|
|
27
22
|
}) => void;
|
|
@@ -36,7 +31,10 @@ interface Props {
|
|
|
36
31
|
gridColor?: string;
|
|
37
32
|
gridLineWidth?: number;
|
|
38
33
|
detectionConfig?: DetectionConfig;
|
|
39
|
-
useNativeOverlay?: boolean;
|
|
40
34
|
}
|
|
35
|
+
type DocScannerHandle = {
|
|
36
|
+
capture: () => Promise<PictureEvent>;
|
|
37
|
+
reset: () => void;
|
|
38
|
+
};
|
|
41
39
|
export declare const DocScanner: React.ForwardRefExoticComponent<Props & React.RefAttributes<DocScannerHandle>>;
|
|
42
40
|
export type { DocScannerHandle };
|
package/dist/DocScanner.js
CHANGED
|
@@ -32,138 +32,85 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.DocScanner = void 0;
|
|
37
40
|
const react_1 = __importStar(require("react"));
|
|
38
41
|
const react_native_1 = require("react-native");
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const NativeDocScanner = (0, react_native_1.requireNativeComponent)(VIEW_NAME);
|
|
49
|
-
const DEFAULT_OVERLAY_COLOR = '#e7a649';
|
|
50
|
-
const GRID_COLOR_FALLBACK = 'rgba(231, 166, 73, 0.35)';
|
|
51
|
-
exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAULT_OVERLAY_COLOR, autoCapture = true, minStableFrames = 8, enableTorch = false, quality = 90, useBase64 = false, children, showGrid = true, gridColor, gridLineWidth = 2, useNativeOverlay, }, ref) => {
|
|
52
|
-
const viewRef = (0, react_1.useRef)(null);
|
|
53
|
-
const capturingRef = (0, react_1.useRef)(false);
|
|
54
|
-
const [quad, setQuad] = (0, react_1.useState)(null);
|
|
55
|
-
const [stable, setStable] = (0, react_1.useState)(0);
|
|
56
|
-
const [frameSize, setFrameSize] = (0, react_1.useState)(null);
|
|
57
|
-
const shouldUseNativeOverlay = (0, react_1.useMemo)(() => {
|
|
58
|
-
if (typeof useNativeOverlay === 'boolean') {
|
|
59
|
-
return useNativeOverlay;
|
|
42
|
+
const react_native_document_scanner_1 = __importDefault(require("react-native-document-scanner"));
|
|
43
|
+
const DEFAULT_OVERLAY_COLOR = '#0b7ef4';
|
|
44
|
+
exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAULT_OVERLAY_COLOR, autoCapture = true, minStableFrames = 8, enableTorch = false, quality = 90, useBase64 = false, children, showGrid = true, }, ref) => {
|
|
45
|
+
const scannerRef = (0, react_1.useRef)(null);
|
|
46
|
+
const captureResolvers = (0, react_1.useRef)(null);
|
|
47
|
+
const normalizedQuality = (0, react_1.useMemo)(() => {
|
|
48
|
+
if (react_native_1.Platform.OS === 'ios') {
|
|
49
|
+
// iOS expects 0-1
|
|
50
|
+
return Math.min(1, Math.max(0, quality / 100));
|
|
60
51
|
}
|
|
61
|
-
return
|
|
62
|
-
}, [
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
return Math.min(100, Math.max(0, quality));
|
|
53
|
+
}, [quality]);
|
|
54
|
+
const handlePictureTaken = (0, react_1.useCallback)((event) => {
|
|
55
|
+
const path = event.croppedImage ?? event.initialImage;
|
|
56
|
+
if (path) {
|
|
57
|
+
onCapture?.({
|
|
58
|
+
path,
|
|
59
|
+
quad: null,
|
|
60
|
+
width: event.width ?? 0,
|
|
61
|
+
height: event.height ?? 0,
|
|
62
|
+
});
|
|
68
63
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const handle = ensureViewHandle();
|
|
74
|
-
NativeDocScannerModule.reset(handle);
|
|
64
|
+
if (captureResolvers.current) {
|
|
65
|
+
captureResolvers.current.resolve(event);
|
|
66
|
+
captureResolvers.current = null;
|
|
75
67
|
}
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
}, [onCapture]);
|
|
69
|
+
const handleError = (0, react_1.useCallback)((error) => {
|
|
70
|
+
if (captureResolvers.current) {
|
|
71
|
+
captureResolvers.current.reject(error);
|
|
72
|
+
captureResolvers.current = null;
|
|
78
73
|
}
|
|
79
|
-
}, [
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const width = payload.width ?? frameSize?.width ?? 0;
|
|
87
|
-
const height = payload.height ?? frameSize?.height ?? 0;
|
|
88
|
-
onCapture?.({
|
|
89
|
-
path,
|
|
90
|
-
quad,
|
|
91
|
-
width,
|
|
92
|
-
height,
|
|
93
|
-
});
|
|
94
|
-
setStable(0);
|
|
95
|
-
resetNativeStability();
|
|
96
|
-
}, [frameSize, onCapture, quad, resetNativeStability]);
|
|
97
|
-
const handleRectangleDetect = (0, react_1.useCallback)((event) => {
|
|
98
|
-
const { rectangleCoordinates, stableCounter, frameWidth, frameHeight } = event.nativeEvent;
|
|
99
|
-
setStable(stableCounter);
|
|
100
|
-
setFrameSize({ width: frameWidth, height: frameHeight });
|
|
101
|
-
if (!rectangleCoordinates) {
|
|
102
|
-
setQuad(null);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
setQuad([
|
|
106
|
-
rectangleCoordinates.topLeft,
|
|
107
|
-
rectangleCoordinates.topRight,
|
|
108
|
-
rectangleCoordinates.bottomRight,
|
|
109
|
-
rectangleCoordinates.bottomLeft,
|
|
110
|
-
]);
|
|
111
|
-
if (autoCapture && stableCounter >= minStableFrames) {
|
|
112
|
-
triggerCapture();
|
|
74
|
+
}, []);
|
|
75
|
+
const capture = (0, react_1.useCallback)(() => {
|
|
76
|
+
const instance = scannerRef.current;
|
|
77
|
+
if (!instance || typeof instance.capture !== 'function') {
|
|
78
|
+
return Promise.reject(new Error('DocumentScanner native instance is not ready'));
|
|
113
79
|
}
|
|
114
|
-
|
|
115
|
-
const handlePictureTaken = (0, react_1.useCallback)((event) => {
|
|
116
|
-
emitCaptureResult(event.nativeEvent);
|
|
117
|
-
}, [emitCaptureResult]);
|
|
118
|
-
const captureNative = (0, react_1.useCallback)(() => {
|
|
119
|
-
if (capturingRef.current) {
|
|
80
|
+
if (captureResolvers.current) {
|
|
120
81
|
return Promise.reject(new Error('capture_in_progress'));
|
|
121
82
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
emitCaptureResult(result);
|
|
128
|
-
return result;
|
|
129
|
-
})
|
|
130
|
-
.catch((error) => {
|
|
131
|
-
capturingRef.current = false;
|
|
132
|
-
throw error;
|
|
83
|
+
const result = instance.capture();
|
|
84
|
+
if (result && typeof result.then === 'function') {
|
|
85
|
+
return result.then((payload) => {
|
|
86
|
+
handlePictureTaken(payload);
|
|
87
|
+
return payload;
|
|
133
88
|
});
|
|
134
89
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return Promise.reject(error);
|
|
138
|
-
}
|
|
139
|
-
}, [emitCaptureResult, ensureViewHandle]);
|
|
140
|
-
const triggerCapture = (0, react_1.useCallback)(() => {
|
|
141
|
-
if (capturingRef.current) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
captureNative().catch((error) => {
|
|
145
|
-
console.warn('[DocScanner] capture failed', error);
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
captureResolvers.current = { resolve, reject };
|
|
146
92
|
});
|
|
147
|
-
}, [
|
|
93
|
+
}, [handlePictureTaken]);
|
|
148
94
|
const handleManualCapture = (0, react_1.useCallback)(() => {
|
|
149
95
|
if (autoCapture) {
|
|
150
96
|
return;
|
|
151
97
|
}
|
|
152
|
-
|
|
98
|
+
capture().catch((error) => {
|
|
153
99
|
console.warn('[DocScanner] manual capture failed', error);
|
|
154
100
|
});
|
|
155
|
-
}, [autoCapture,
|
|
101
|
+
}, [autoCapture, capture]);
|
|
156
102
|
(0, react_1.useImperativeHandle)(ref, () => ({
|
|
157
|
-
capture
|
|
103
|
+
capture,
|
|
158
104
|
reset: () => {
|
|
159
|
-
|
|
160
|
-
|
|
105
|
+
if (captureResolvers.current) {
|
|
106
|
+
captureResolvers.current.reject(new Error('reset'));
|
|
107
|
+
captureResolvers.current = null;
|
|
108
|
+
}
|
|
161
109
|
},
|
|
162
|
-
}), [
|
|
110
|
+
}), [capture]);
|
|
163
111
|
return (react_1.default.createElement(react_native_1.View, { style: styles.container },
|
|
164
|
-
react_1.default.createElement(
|
|
165
|
-
!
|
|
166
|
-
!autoCapture && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.button, onPress: handleManualCapture })),
|
|
112
|
+
react_1.default.createElement(react_native_document_scanner_1.default, { ref: scannerRef, style: react_native_1.StyleSheet.absoluteFillObject, detectionCountBeforeCapture: minStableFrames, overlayColor: overlayColor, enableTorch: enableTorch, quality: normalizedQuality, useBase64: useBase64, manualOnly: !autoCapture, onPictureTaken: handlePictureTaken, onError: handleError }),
|
|
113
|
+
!autoCapture && react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.button, onPress: handleManualCapture }),
|
|
167
114
|
children));
|
|
168
115
|
});
|
|
169
116
|
const styles = react_native_1.StyleSheet.create({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-rectangle-doc-scanner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Native-backed document scanner for React Native with customizable overlays.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc",
|
|
18
|
-
"prepare": "yarn build"
|
|
18
|
+
"prepare": "yarn build",
|
|
19
|
+
"postinstall": "patch-package"
|
|
19
20
|
},
|
|
20
21
|
"peerDependencies": {
|
|
21
22
|
"@shopify/react-native-skia": "*",
|
|
@@ -26,7 +27,10 @@
|
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@types/react": "^18.2.41",
|
|
28
29
|
"@types/react-native": "0.73.0",
|
|
30
|
+
"patch-package": "^8.0.0",
|
|
29
31
|
"typescript": "^5.3.3"
|
|
30
32
|
},
|
|
31
|
-
"dependencies": {
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"react-native-document-scanner": "github:Michaelvilleneuve/react-native-document-scanner"
|
|
35
|
+
}
|
|
32
36
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
diff --git a/node_modules/react-native-document-scanner/ios/IPDFCameraViewController.m b/node_modules/react-native-document-scanner/ios/IPDFCameraViewController.m
|
|
2
|
+
index 1234567..abcdefg 100644
|
|
3
|
+
--- a/node_modules/react-native-document-scanner/ios/IPDFCameraViewController.m
|
|
4
|
+
+++ b/node_modules/react-native-document-scanner/ios/IPDFCameraViewController.m
|
|
5
|
+
@@ -115,7 +115,16 @@
|
|
6
|
+
|
|
7
|
+
NSError *error = nil;
|
|
8
|
+
AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
|
|
9
|
+
- session.sessionPreset = AVCaptureSessionPresetPhoto;
|
|
10
|
+
+
|
|
11
|
+
+ // Try to use the highest quality preset available
|
|
12
|
+
+ if ([session canSetSessionPreset:AVCaptureSessionPreset3840x2160]) {
|
|
13
|
+
+ session.sessionPreset = AVCaptureSessionPreset3840x2160; // 4K
|
|
14
|
+
+ } else if ([session canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {
|
|
15
|
+
+ session.sessionPreset = AVCaptureSessionPreset1920x1080; // Full HD
|
|
16
|
+
+ } else {
|
|
17
|
+
+ session.sessionPreset = AVCaptureSessionPresetPhoto; // Fallback
|
|
18
|
+
+ }
|
|
19
|
+
+
|
|
20
|
+
[session addInput:input];
|
|
21
|
+
|
|
22
|
+
AVCaptureVideoDataOutput *dataOutput = [[AVCaptureVideoDataOutput alloc] init];
|