rn-document-scanner-vision 1.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/LICENSE +21 -0
- package/README.md +258 -0
- package/android/build.gradle +43 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradlew +248 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/rndocumentscanner/RNDocumentScannerModule.java +126 -0
- package/android/src/main/java/com/rndocumentscanner/RNDocumentScannerPackage.java +25 -0
- package/ios/RNDocumentScanner.h +5 -0
- package/ios/RNDocumentScanner.m +156 -0
- package/lib/commonjs/index.js +73 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +67 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/index.d.ts +76 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +81 -0
- package/react-native-document-scanner.podspec +21 -0
- package/src/index.tsx +112 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hoang Thien
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# React Native Document Scanner
|
|
2
|
+
|
|
3
|
+
A React Native library for scanning documents on iOS and Android. This library provides a native document scanning experience using VisionKit for iOS and a document scanner library for Android.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📷 Native document scanning on iOS (VisionKit) and Android
|
|
8
|
+
- 🔲 Automatic document detection and edge detection
|
|
9
|
+
- ✂️ Automatic cropping and perspective correction
|
|
10
|
+
- 📄 Support for scanning multiple documents (Android)
|
|
11
|
+
- 🎨 Adjustable image quality
|
|
12
|
+
- 📤 Export as base64 or file paths
|
|
13
|
+
- 🎯 User-friendly interface with native look and feel
|
|
14
|
+
- ⚡ Full TypeScript support
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install react-native-document-scanner
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
or
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
yarn add react-native-document-scanner
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### iOS Setup
|
|
29
|
+
|
|
30
|
+
1. **Minimum iOS Version**: iOS 13.0+
|
|
31
|
+
|
|
32
|
+
2. **Install CocoaPods dependencies**:
|
|
33
|
+
```sh
|
|
34
|
+
cd ios && pod install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
3. **Add Camera Permission**: Add the following to your `ios/YourApp/Info.plist`:
|
|
38
|
+
```xml
|
|
39
|
+
<key>NSCameraUsageDescription</key>
|
|
40
|
+
<string>We need camera access to scan documents</string>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Android Setup
|
|
44
|
+
|
|
45
|
+
1. **Minimum SDK**: 21
|
|
46
|
+
|
|
47
|
+
2. **Add Camera Permission**: The library already includes the necessary permissions in its AndroidManifest.xml, but make sure your app requests runtime permissions. Add the following to your `android/app/src/main/AndroidManifest.xml` if not already present:
|
|
48
|
+
```xml
|
|
49
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. **Request Runtime Permissions**: In your React Native component, request camera permission before scanning:
|
|
53
|
+
```javascript
|
|
54
|
+
import { PermissionsAndroid, Platform } from 'react-native';
|
|
55
|
+
|
|
56
|
+
async function requestCameraPermission() {
|
|
57
|
+
if (Platform.OS === 'android') {
|
|
58
|
+
try {
|
|
59
|
+
const granted = await PermissionsAndroid.request(
|
|
60
|
+
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
61
|
+
{
|
|
62
|
+
title: 'Camera Permission',
|
|
63
|
+
message: 'App needs camera permission to scan documents',
|
|
64
|
+
buttonNeutral: 'Ask Me Later',
|
|
65
|
+
buttonNegative: 'Cancel',
|
|
66
|
+
buttonPositive: 'OK',
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.warn(err);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
### Basic Example
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import React from 'react';
|
|
85
|
+
import { Button, View, Text, Image } from 'react-native';
|
|
86
|
+
import { DocumentScanner } from 'react-native-document-scanner';
|
|
87
|
+
|
|
88
|
+
function App() {
|
|
89
|
+
const [scannedImages, setScannedImages] = React.useState<string[]>([]);
|
|
90
|
+
|
|
91
|
+
const handleScan = async () => {
|
|
92
|
+
try {
|
|
93
|
+
const result = await DocumentScanner.scanDocument({
|
|
94
|
+
croppedImageQuality: 100,
|
|
95
|
+
responseType: 'imageFilePath',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (result.status === 'success') {
|
|
99
|
+
setScannedImages(result.scannedImages);
|
|
100
|
+
console.log('Scanned images:', result.scannedImages);
|
|
101
|
+
} else {
|
|
102
|
+
console.log('User cancelled the scan');
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('Error scanning document:', error);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<View>
|
|
111
|
+
<Button title="Scan Document" onPress={handleScan} />
|
|
112
|
+
{scannedImages.map((image, index) => (
|
|
113
|
+
<Image key={index} source={{ uri: image }} style={{ width: 200, height: 300 }} />
|
|
114
|
+
))}
|
|
115
|
+
</View>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Advanced Example with All Options
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { DocumentScanner } from 'react-native-document-scanner';
|
|
124
|
+
|
|
125
|
+
async function scanMultipleDocuments() {
|
|
126
|
+
try {
|
|
127
|
+
const result = await DocumentScanner.scanDocument({
|
|
128
|
+
maxNumDocuments: 3, // Android only: max number of documents to scan
|
|
129
|
+
letUserAdjustCrop: true, // Android only: allow manual crop adjustment
|
|
130
|
+
croppedImageQuality: 100, // Image quality (0-100)
|
|
131
|
+
responseType: 'base64', // 'base64' or 'imageFilePath'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (result.status === 'success') {
|
|
135
|
+
// Process scanned images
|
|
136
|
+
result.scannedImages.forEach((image, index) => {
|
|
137
|
+
console.log(`Image ${index + 1}:`, image.substring(0, 50) + '...');
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('Scan failed:', error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## API Reference
|
|
147
|
+
|
|
148
|
+
### `DocumentScanner.scanDocument(options?)`
|
|
149
|
+
|
|
150
|
+
Starts the document scanning process.
|
|
151
|
+
|
|
152
|
+
#### Parameters
|
|
153
|
+
|
|
154
|
+
- `options` (optional): Configuration object
|
|
155
|
+
|
|
156
|
+
| Option | Type | Default | Platform | Description |
|
|
157
|
+
|--------|------|---------|----------|-------------|
|
|
158
|
+
| `maxNumDocuments` | `number` | `1` | Android | Maximum number of documents to scan |
|
|
159
|
+
| `letUserAdjustCrop` | `boolean` | `true` | Android | Allow user to manually adjust crop |
|
|
160
|
+
| `croppedImageQuality` | `number` | `100` | Both | Image quality from 0 to 100 |
|
|
161
|
+
| `responseType` | `'base64' \| 'imageFilePath'` | `'imageFilePath'` | Both | Format of returned images |
|
|
162
|
+
|
|
163
|
+
#### Returns
|
|
164
|
+
|
|
165
|
+
`Promise<ScanDocumentResponse>`
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
interface ScanDocumentResponse {
|
|
169
|
+
scannedImages: string[]; // Array of base64 strings or file paths
|
|
170
|
+
status: 'success' | 'cancel';
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Errors
|
|
175
|
+
|
|
176
|
+
The promise will be rejected with an error if:
|
|
177
|
+
- Camera permission is denied
|
|
178
|
+
- Document scanning is not supported on the device
|
|
179
|
+
- An error occurs during scanning
|
|
180
|
+
- Device is running iOS < 13.0
|
|
181
|
+
|
|
182
|
+
## TypeScript Support
|
|
183
|
+
|
|
184
|
+
This library is written in TypeScript and includes type definitions out of the box.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import {
|
|
188
|
+
DocumentScanner,
|
|
189
|
+
ScanDocumentOptions,
|
|
190
|
+
ScanDocumentResponse
|
|
191
|
+
} from 'react-native-document-scanner';
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Platform Differences
|
|
195
|
+
|
|
196
|
+
### iOS
|
|
197
|
+
- Uses VisionKit's `VNDocumentCameraViewController`
|
|
198
|
+
- Automatically detects document edges
|
|
199
|
+
- Provides built-in crop and perspective correction
|
|
200
|
+
- Returns multiple pages if user scans multiple documents
|
|
201
|
+
- Options `maxNumDocuments` and `letUserAdjustCrop` are ignored
|
|
202
|
+
|
|
203
|
+
### Android
|
|
204
|
+
- Uses `com.websitebeaver:documentscanner` library
|
|
205
|
+
- Supports configurable maximum number of documents
|
|
206
|
+
- Allows user to adjust crop boundaries
|
|
207
|
+
- Supports all configuration options
|
|
208
|
+
|
|
209
|
+
## Troubleshooting
|
|
210
|
+
|
|
211
|
+
### iOS Issues
|
|
212
|
+
|
|
213
|
+
**Problem**: "Document scanning is not supported on this device"
|
|
214
|
+
- **Solution**: VisionKit document scanning requires iOS 13.0 or later. Ensure your deployment target is iOS 13.0+.
|
|
215
|
+
|
|
216
|
+
**Problem**: Camera permission denied
|
|
217
|
+
- **Solution**: Make sure you've added `NSCameraUsageDescription` to your Info.plist.
|
|
218
|
+
|
|
219
|
+
**Problem**: Module not found
|
|
220
|
+
- **Solution**: Run `cd ios && pod install && cd ..` and rebuild your app.
|
|
221
|
+
|
|
222
|
+
### Android Issues
|
|
223
|
+
|
|
224
|
+
**Problem**: Camera permission denied
|
|
225
|
+
- **Solution**: Request camera permission at runtime using `PermissionsAndroid`.
|
|
226
|
+
|
|
227
|
+
**Problem**: Build errors with Gradle
|
|
228
|
+
- **Solution**: Ensure your `android/build.gradle` has `minSdkVersion` 21 or higher.
|
|
229
|
+
|
|
230
|
+
**Problem**: "Failed to initialize document scanner"
|
|
231
|
+
- **Solution**: Make sure the camera permission is granted before calling `scanDocument`.
|
|
232
|
+
|
|
233
|
+
### General Issues
|
|
234
|
+
|
|
235
|
+
**Problem**: "The package doesn't seem to be linked"
|
|
236
|
+
- **Solution**:
|
|
237
|
+
- For React Native 0.60+: Autolinking should work automatically. Try cleaning and rebuilding.
|
|
238
|
+
- iOS: Run `cd ios && pod install && cd ..`
|
|
239
|
+
- Android: Clean gradle cache `cd android && ./gradlew clean && cd ..`
|
|
240
|
+
- Rebuild the app completely
|
|
241
|
+
|
|
242
|
+
## Contributing
|
|
243
|
+
|
|
244
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
249
|
+
|
|
250
|
+
## Credits
|
|
251
|
+
|
|
252
|
+
This library is based on the Capacitor Document Scanner plugin and uses:
|
|
253
|
+
- iOS: VisionKit framework by Apple
|
|
254
|
+
- Android: [Document Scanner Library](https://github.com/websitebeaver/documentscanner) by WebsiteBeaver
|
|
255
|
+
|
|
256
|
+
## Support
|
|
257
|
+
|
|
258
|
+
For issues and feature requests, please use the [GitHub issue tracker](https://github.com/hoangthien123/react-native-document-scanner/issues).
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
plugins {
|
|
2
|
+
id 'com.android.library'
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
android {
|
|
6
|
+
compileSdk 34
|
|
7
|
+
|
|
8
|
+
namespace "com.rndocumentscanner"
|
|
9
|
+
|
|
10
|
+
defaultConfig {
|
|
11
|
+
minSdk 21
|
|
12
|
+
targetSdk 34
|
|
13
|
+
versionCode 1
|
|
14
|
+
versionName "1.0"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
buildTypes {
|
|
18
|
+
release {
|
|
19
|
+
minifyEnabled false
|
|
20
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
compileOptions {
|
|
25
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
26
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
lint {
|
|
30
|
+
abortOnError false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
repositories {
|
|
35
|
+
google()
|
|
36
|
+
mavenCentral()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dependencies {
|
|
40
|
+
implementation 'com.facebook.react:react-android'
|
|
41
|
+
implementation "androidx.appcompat:appcompat:1.7.0"
|
|
42
|
+
implementation "com.websitebeaver:documentscanner:1.3.4"
|
|
43
|
+
}
|
|
Binary file
|
package/android/gradlew
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright © 2015 the original authors.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
19
|
+
#
|
|
20
|
+
|
|
21
|
+
##############################################################################
|
|
22
|
+
#
|
|
23
|
+
# Gradle start up script for POSIX generated by Gradle.
|
|
24
|
+
#
|
|
25
|
+
# Important for running:
|
|
26
|
+
#
|
|
27
|
+
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
|
28
|
+
# noncompliant, but you have some other compliant shell such as ksh or
|
|
29
|
+
# bash, then to run this script, type that shell name before the whole
|
|
30
|
+
# command line, like:
|
|
31
|
+
#
|
|
32
|
+
# ksh Gradle
|
|
33
|
+
#
|
|
34
|
+
# Busybox and similar reduced shells will NOT work, because this script
|
|
35
|
+
# requires all of these POSIX shell features:
|
|
36
|
+
# * functions;
|
|
37
|
+
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
|
38
|
+
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
|
39
|
+
# * compound commands having a testable exit status, especially «case»;
|
|
40
|
+
# * various built-in commands including «command», «set», and «ulimit».
|
|
41
|
+
#
|
|
42
|
+
# Important for patching:
|
|
43
|
+
#
|
|
44
|
+
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
|
45
|
+
# by Bash, Ksh, etc; in particular arrays are avoided.
|
|
46
|
+
#
|
|
47
|
+
# The "traditional" practice of packing multiple parameters into a
|
|
48
|
+
# space-separated string is a well documented source of bugs and security
|
|
49
|
+
# problems, so this is (mostly) avoided, by progressively accumulating
|
|
50
|
+
# options in "$@", and eventually passing that to Java.
|
|
51
|
+
#
|
|
52
|
+
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
|
53
|
+
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
|
54
|
+
# see the in-line comments for details.
|
|
55
|
+
#
|
|
56
|
+
# There are tweaks for specific operating systems such as AIX, CygWin,
|
|
57
|
+
# Darwin, MinGW, and NonStop.
|
|
58
|
+
#
|
|
59
|
+
# (3) This script is generated from the Groovy template
|
|
60
|
+
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
|
61
|
+
# within the Gradle project.
|
|
62
|
+
#
|
|
63
|
+
# You can find Gradle at https://github.com/gradle/gradle/.
|
|
64
|
+
#
|
|
65
|
+
##############################################################################
|
|
66
|
+
|
|
67
|
+
# Attempt to set APP_HOME
|
|
68
|
+
|
|
69
|
+
# Resolve links: $0 may be a link
|
|
70
|
+
app_path=$0
|
|
71
|
+
|
|
72
|
+
# Need this for daisy-chained symlinks.
|
|
73
|
+
while
|
|
74
|
+
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
|
75
|
+
[ -h "$app_path" ]
|
|
76
|
+
do
|
|
77
|
+
ls=$( ls -ld "$app_path" )
|
|
78
|
+
link=${ls#*' -> '}
|
|
79
|
+
case $link in #(
|
|
80
|
+
/*) app_path=$link ;; #(
|
|
81
|
+
*) app_path=$APP_HOME$link ;;
|
|
82
|
+
esac
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
# This is normally unused
|
|
86
|
+
# shellcheck disable=SC2034
|
|
87
|
+
APP_BASE_NAME=${0##*/}
|
|
88
|
+
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
|
89
|
+
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
|
90
|
+
|
|
91
|
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
92
|
+
MAX_FD=maximum
|
|
93
|
+
|
|
94
|
+
warn () {
|
|
95
|
+
echo "$*"
|
|
96
|
+
} >&2
|
|
97
|
+
|
|
98
|
+
die () {
|
|
99
|
+
echo
|
|
100
|
+
echo "$*"
|
|
101
|
+
echo
|
|
102
|
+
exit 1
|
|
103
|
+
} >&2
|
|
104
|
+
|
|
105
|
+
# OS specific support (must be 'true' or 'false').
|
|
106
|
+
cygwin=false
|
|
107
|
+
msys=false
|
|
108
|
+
darwin=false
|
|
109
|
+
nonstop=false
|
|
110
|
+
case "$( uname )" in #(
|
|
111
|
+
CYGWIN* ) cygwin=true ;; #(
|
|
112
|
+
Darwin* ) darwin=true ;; #(
|
|
113
|
+
MSYS* | MINGW* ) msys=true ;; #(
|
|
114
|
+
NONSTOP* ) nonstop=true ;;
|
|
115
|
+
esac
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Determine the Java command to use to start the JVM.
|
|
120
|
+
if [ -n "$JAVA_HOME" ] ; then
|
|
121
|
+
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
|
122
|
+
# IBM's JDK on AIX uses strange locations for the executables
|
|
123
|
+
JAVACMD=$JAVA_HOME/jre/sh/java
|
|
124
|
+
else
|
|
125
|
+
JAVACMD=$JAVA_HOME/bin/java
|
|
126
|
+
fi
|
|
127
|
+
if [ ! -x "$JAVACMD" ] ; then
|
|
128
|
+
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
|
129
|
+
|
|
130
|
+
Please set the JAVA_HOME variable in your environment to match the
|
|
131
|
+
location of your Java installation."
|
|
132
|
+
fi
|
|
133
|
+
else
|
|
134
|
+
JAVACMD=java
|
|
135
|
+
if ! command -v java >/dev/null 2>&1
|
|
136
|
+
then
|
|
137
|
+
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
138
|
+
|
|
139
|
+
Please set the JAVA_HOME variable in your environment to match the
|
|
140
|
+
location of your Java installation."
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Increase the maximum file descriptors if we can.
|
|
145
|
+
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|
146
|
+
case $MAX_FD in #(
|
|
147
|
+
max*)
|
|
148
|
+
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
|
149
|
+
# shellcheck disable=SC2039,SC3045
|
|
150
|
+
MAX_FD=$( ulimit -H -n ) ||
|
|
151
|
+
warn "Could not query maximum file descriptor limit"
|
|
152
|
+
esac
|
|
153
|
+
case $MAX_FD in #(
|
|
154
|
+
'' | soft) :;; #(
|
|
155
|
+
*)
|
|
156
|
+
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
|
157
|
+
# shellcheck disable=SC2039,SC3045
|
|
158
|
+
ulimit -n "$MAX_FD" ||
|
|
159
|
+
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
|
160
|
+
esac
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# Collect all arguments for the java command, stacking in reverse order:
|
|
164
|
+
# * args from the command line
|
|
165
|
+
# * the main class name
|
|
166
|
+
# * -classpath
|
|
167
|
+
# * -D...appname settings
|
|
168
|
+
# * --module-path (only if needed)
|
|
169
|
+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
|
170
|
+
|
|
171
|
+
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
172
|
+
if "$cygwin" || "$msys" ; then
|
|
173
|
+
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
|
174
|
+
|
|
175
|
+
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
|
176
|
+
|
|
177
|
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
178
|
+
for arg do
|
|
179
|
+
if
|
|
180
|
+
case $arg in #(
|
|
181
|
+
-*) false ;; # don't mess with options #(
|
|
182
|
+
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
|
183
|
+
[ -e "$t" ] ;; #(
|
|
184
|
+
*) false ;;
|
|
185
|
+
esac
|
|
186
|
+
then
|
|
187
|
+
arg=$( cygpath --path --ignore --mixed "$arg" )
|
|
188
|
+
fi
|
|
189
|
+
# Roll the args list around exactly as many times as the number of
|
|
190
|
+
# args, so each arg winds up back in the position where it started, but
|
|
191
|
+
# possibly modified.
|
|
192
|
+
#
|
|
193
|
+
# NB: a `for` loop captures its iteration list before it begins, so
|
|
194
|
+
# changing the positional parameters here affects neither the number of
|
|
195
|
+
# iterations, nor the values presented in `arg`.
|
|
196
|
+
shift # remove old arg
|
|
197
|
+
set -- "$@" "$arg" # push replacement arg
|
|
198
|
+
done
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
203
|
+
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
204
|
+
|
|
205
|
+
# Collect all arguments for the java command:
|
|
206
|
+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
|
207
|
+
# and any embedded shellness will be escaped.
|
|
208
|
+
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
|
209
|
+
# treated as '${Hostname}' itself on the command line.
|
|
210
|
+
|
|
211
|
+
set -- \
|
|
212
|
+
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
|
213
|
+
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
|
214
|
+
"$@"
|
|
215
|
+
|
|
216
|
+
# Stop when "xargs" is not available.
|
|
217
|
+
if ! command -v xargs >/dev/null 2>&1
|
|
218
|
+
then
|
|
219
|
+
die "xargs is not available"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# Use "xargs" to parse quoted args.
|
|
223
|
+
#
|
|
224
|
+
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
|
225
|
+
#
|
|
226
|
+
# In Bash we could simply go:
|
|
227
|
+
#
|
|
228
|
+
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
|
229
|
+
# set -- "${ARGS[@]}" "$@"
|
|
230
|
+
#
|
|
231
|
+
# but POSIX shell has neither arrays nor command substitution, so instead we
|
|
232
|
+
# post-process each arg (as a line of input to sed) to backslash-escape any
|
|
233
|
+
# character that might be a shell metacharacter, then use eval to reverse
|
|
234
|
+
# that process (while maintaining the separation between arguments), and wrap
|
|
235
|
+
# the whole thing up as a single "set" statement.
|
|
236
|
+
#
|
|
237
|
+
# This will of course break if any of these variables contains a newline or
|
|
238
|
+
# an unmatched quote.
|
|
239
|
+
#
|
|
240
|
+
|
|
241
|
+
eval "set -- $(
|
|
242
|
+
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
|
243
|
+
xargs -n1 |
|
|
244
|
+
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
|
245
|
+
tr '\n' ' '
|
|
246
|
+
)" '"$@"'
|
|
247
|
+
|
|
248
|
+
exec "$JAVACMD" "$@"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
3
|
+
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
|
4
|
+
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
|
5
|
+
</manifest>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
package com.rndocumentscanner;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.Intent;
|
|
5
|
+
import androidx.activity.result.ActivityResult;
|
|
6
|
+
|
|
7
|
+
import com.facebook.react.bridge.ActivityEventListener;
|
|
8
|
+
import com.facebook.react.bridge.Arguments;
|
|
9
|
+
import com.facebook.react.bridge.BaseActivityEventListener;
|
|
10
|
+
import com.facebook.react.bridge.Promise;
|
|
11
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
12
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
13
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
14
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
15
|
+
import com.facebook.react.bridge.WritableArray;
|
|
16
|
+
import com.facebook.react.bridge.WritableMap;
|
|
17
|
+
|
|
18
|
+
import com.websitebeaver.documentscanner.DocumentScanner;
|
|
19
|
+
import com.websitebeaver.documentscanner.constants.DocumentScannerExtra;
|
|
20
|
+
|
|
21
|
+
import java.util.ArrayList;
|
|
22
|
+
|
|
23
|
+
public class RNDocumentScannerModule extends ReactContextBaseJavaModule {
|
|
24
|
+
|
|
25
|
+
private static final int DOCUMENT_SCAN_REQUEST = 1;
|
|
26
|
+
private Promise scanPromise;
|
|
27
|
+
private DocumentScanner documentScanner;
|
|
28
|
+
|
|
29
|
+
private final ActivityEventListener activityEventListener = new BaseActivityEventListener() {
|
|
30
|
+
@Override
|
|
31
|
+
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
32
|
+
if (requestCode == DOCUMENT_SCAN_REQUEST) {
|
|
33
|
+
if (documentScanner != null) {
|
|
34
|
+
// Use a wrapper ActivityResult object
|
|
35
|
+
ActivityResult result = new ActivityResult(resultCode, data);
|
|
36
|
+
documentScanner.handleDocumentScanIntentResult(result);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
public RNDocumentScannerModule(ReactApplicationContext reactContext) {
|
|
43
|
+
super(reactContext);
|
|
44
|
+
reactContext.addActivityEventListener(activityEventListener);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Override
|
|
48
|
+
public String getName() {
|
|
49
|
+
return "RNDocumentScanner";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@ReactMethod
|
|
53
|
+
public void scanDocument(ReadableMap options, Promise promise) {
|
|
54
|
+
Activity activity = getCurrentActivity();
|
|
55
|
+
|
|
56
|
+
if (activity == null) {
|
|
57
|
+
promise.reject("NO_ACTIVITY", "Activity doesn't exist");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
scanPromise = promise;
|
|
62
|
+
|
|
63
|
+
// Get options with defaults
|
|
64
|
+
String responseType = options.hasKey("responseType") ? options.getString("responseType") : "imageFilePath";
|
|
65
|
+
Boolean letUserAdjustCrop = options.hasKey("letUserAdjustCrop") ? options.getBoolean("letUserAdjustCrop") : true;
|
|
66
|
+
Integer maxNumDocuments = options.hasKey("maxNumDocuments") ? options.getInt("maxNumDocuments") : 1;
|
|
67
|
+
Integer croppedImageQuality = options.hasKey("croppedImageQuality") ? options.getInt("croppedImageQuality") : 100;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
// Create document scanner with callbacks
|
|
71
|
+
documentScanner = new DocumentScanner(
|
|
72
|
+
activity,
|
|
73
|
+
(ArrayList<String> documentScanResults) -> {
|
|
74
|
+
// Success callback
|
|
75
|
+
WritableMap response = Arguments.createMap();
|
|
76
|
+
WritableArray scannedImages = Arguments.createArray();
|
|
77
|
+
|
|
78
|
+
for (String imagePath : documentScanResults) {
|
|
79
|
+
scannedImages.pushString(imagePath);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
response.putArray("scannedImages", scannedImages);
|
|
83
|
+
response.putString("status", "success");
|
|
84
|
+
|
|
85
|
+
if (scanPromise != null) {
|
|
86
|
+
scanPromise.resolve(response);
|
|
87
|
+
scanPromise = null;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
},
|
|
91
|
+
(String errorMessage) -> {
|
|
92
|
+
// Error callback
|
|
93
|
+
if (scanPromise != null) {
|
|
94
|
+
scanPromise.reject("SCAN_ERROR", errorMessage);
|
|
95
|
+
scanPromise = null;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
},
|
|
99
|
+
() -> {
|
|
100
|
+
// Cancel callback
|
|
101
|
+
WritableMap response = Arguments.createMap();
|
|
102
|
+
WritableArray emptyArray = Arguments.createArray();
|
|
103
|
+
response.putArray("scannedImages", emptyArray);
|
|
104
|
+
response.putString("status", "cancel");
|
|
105
|
+
|
|
106
|
+
if (scanPromise != null) {
|
|
107
|
+
scanPromise.resolve(response);
|
|
108
|
+
scanPromise = null;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
},
|
|
112
|
+
responseType,
|
|
113
|
+
letUserAdjustCrop,
|
|
114
|
+
maxNumDocuments,
|
|
115
|
+
croppedImageQuality
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Launch the document scanner
|
|
119
|
+
Intent scanIntent = documentScanner.createDocumentScanIntent();
|
|
120
|
+
activity.startActivityForResult(scanIntent, DOCUMENT_SCAN_REQUEST);
|
|
121
|
+
|
|
122
|
+
} catch (Exception e) {
|
|
123
|
+
promise.reject("INITIALIZATION_ERROR", "Failed to initialize document scanner: " + e.getMessage());
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.rndocumentscanner;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage;
|
|
4
|
+
import com.facebook.react.bridge.NativeModule;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
7
|
+
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.Collections;
|
|
10
|
+
import java.util.List;
|
|
11
|
+
|
|
12
|
+
public class RNDocumentScannerPackage implements ReactPackage {
|
|
13
|
+
|
|
14
|
+
@Override
|
|
15
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
16
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
17
|
+
modules.add(new RNDocumentScannerModule(reactContext));
|
|
18
|
+
return modules;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Override
|
|
22
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
23
|
+
return Collections.emptyList();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#import "RNDocumentScanner.h"
|
|
2
|
+
#import <React/RCTLog.h>
|
|
3
|
+
#import <React/RCTUtils.h>
|
|
4
|
+
#import <VisionKit/VisionKit.h>
|
|
5
|
+
|
|
6
|
+
@interface RNDocumentScanner () <VNDocumentCameraViewControllerDelegate>
|
|
7
|
+
|
|
8
|
+
@property (nonatomic, strong) RCTPromiseResolveBlock resolveBlock;
|
|
9
|
+
@property (nonatomic, strong) RCTPromiseRejectBlock rejectBlock;
|
|
10
|
+
@property (nonatomic, assign) NSInteger croppedImageQuality;
|
|
11
|
+
@property (nonatomic, strong) NSString *responseType;
|
|
12
|
+
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
@implementation RNDocumentScanner
|
|
16
|
+
|
|
17
|
+
RCT_EXPORT_MODULE();
|
|
18
|
+
|
|
19
|
+
+ (BOOL)requiresMainQueueSetup
|
|
20
|
+
{
|
|
21
|
+
return YES;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
RCT_EXPORT_METHOD(scanDocument:(NSDictionary *)options
|
|
25
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
26
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
27
|
+
{
|
|
28
|
+
// Check iOS version
|
|
29
|
+
if (@available(iOS 13.0, *)) {
|
|
30
|
+
// Check if document scanning is supported
|
|
31
|
+
if (![VNDocumentCameraViewController isSupported]) {
|
|
32
|
+
reject(@"NOT_SUPPORTED", @"Document scanning is not supported on this device", nil);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Store promise handlers
|
|
37
|
+
self.resolveBlock = resolve;
|
|
38
|
+
self.rejectBlock = reject;
|
|
39
|
+
|
|
40
|
+
// Get options
|
|
41
|
+
self.croppedImageQuality = options[@"croppedImageQuality"] ? [options[@"croppedImageQuality"] integerValue] : 100;
|
|
42
|
+
self.responseType = options[@"responseType"] ?: @"imageFilePath";
|
|
43
|
+
|
|
44
|
+
// Present document scanner on main thread
|
|
45
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
46
|
+
VNDocumentCameraViewController *documentCameraViewController = [[VNDocumentCameraViewController alloc] init];
|
|
47
|
+
documentCameraViewController.delegate = self;
|
|
48
|
+
|
|
49
|
+
UIViewController *rootViewController = RCTPresentedViewController();
|
|
50
|
+
if (rootViewController) {
|
|
51
|
+
[rootViewController presentViewController:documentCameraViewController animated:YES completion:nil];
|
|
52
|
+
} else {
|
|
53
|
+
reject(@"NO_VIEWCONTROLLER", @"No view controller available to present scanner", nil);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
reject(@"IOS_VERSION", @"Document scanning requires iOS 13.0 or later", nil);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#pragma mark - VNDocumentCameraViewControllerDelegate
|
|
62
|
+
|
|
63
|
+
- (void)documentCameraViewController:(VNDocumentCameraViewController *)controller didFinishWithScan:(VNDocumentCameraScan *)scan API_AVAILABLE(ios(13.0))
|
|
64
|
+
{
|
|
65
|
+
NSMutableArray *scannedImages = [NSMutableArray array];
|
|
66
|
+
|
|
67
|
+
// Process each scanned page
|
|
68
|
+
for (NSUInteger i = 0; i < scan.pageCount; i++) {
|
|
69
|
+
UIImage *image = [scan imageOfPageAtIndex:i];
|
|
70
|
+
|
|
71
|
+
// Convert to JPEG with quality
|
|
72
|
+
CGFloat quality = (CGFloat)self.croppedImageQuality / 100.0;
|
|
73
|
+
NSData *imageData = UIImageJPEGRepresentation(image, quality);
|
|
74
|
+
|
|
75
|
+
if (!imageData) {
|
|
76
|
+
[controller dismissViewControllerAnimated:YES completion:^{
|
|
77
|
+
if (self.rejectBlock) {
|
|
78
|
+
self.rejectBlock(@"IMAGE_ERROR", @"Failed to convert scanned image to JPEG", nil);
|
|
79
|
+
self.rejectBlock = nil;
|
|
80
|
+
self.resolveBlock = nil;
|
|
81
|
+
}
|
|
82
|
+
}];
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if ([self.responseType isEqualToString:@"base64"]) {
|
|
87
|
+
// Return base64 encoded image
|
|
88
|
+
NSString *base64String = [imageData base64EncodedStringWithOptions:0];
|
|
89
|
+
[scannedImages addObject:base64String];
|
|
90
|
+
} else {
|
|
91
|
+
// Save to temporary directory and return file path
|
|
92
|
+
NSString *timestamp = [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970] * 1000];
|
|
93
|
+
NSString *fileName = [NSString stringWithFormat:@"scanned_doc_%@_%lu.jpg", timestamp, (unsigned long)i];
|
|
94
|
+
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
|
|
95
|
+
|
|
96
|
+
NSError *error = nil;
|
|
97
|
+
BOOL success = [imageData writeToFile:filePath options:NSDataWritingAtomic error:&error];
|
|
98
|
+
|
|
99
|
+
if (!success) {
|
|
100
|
+
[controller dismissViewControllerAnimated:YES completion:^{
|
|
101
|
+
if (self.rejectBlock) {
|
|
102
|
+
self.rejectBlock(@"FILE_ERROR", [NSString stringWithFormat:@"Failed to save image: %@", error.localizedDescription], error);
|
|
103
|
+
self.rejectBlock = nil;
|
|
104
|
+
self.resolveBlock = nil;
|
|
105
|
+
}
|
|
106
|
+
}];
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Return file:// URL
|
|
111
|
+
NSString *fileURL = [NSURL fileURLWithPath:filePath].absoluteString;
|
|
112
|
+
[scannedImages addObject:fileURL];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Dismiss and resolve
|
|
117
|
+
[controller dismissViewControllerAnimated:YES completion:^{
|
|
118
|
+
if (self.resolveBlock) {
|
|
119
|
+
NSDictionary *response = @{
|
|
120
|
+
@"scannedImages": scannedImages,
|
|
121
|
+
@"status": @"success"
|
|
122
|
+
};
|
|
123
|
+
self.resolveBlock(response);
|
|
124
|
+
self.resolveBlock = nil;
|
|
125
|
+
self.rejectBlock = nil;
|
|
126
|
+
}
|
|
127
|
+
}];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
- (void)documentCameraViewControllerDidCancel:(VNDocumentCameraViewController *)controller API_AVAILABLE(ios(13.0))
|
|
131
|
+
{
|
|
132
|
+
[controller dismissViewControllerAnimated:YES completion:^{
|
|
133
|
+
if (self.resolveBlock) {
|
|
134
|
+
NSDictionary *response = @{
|
|
135
|
+
@"scannedImages": @[],
|
|
136
|
+
@"status": @"cancel"
|
|
137
|
+
};
|
|
138
|
+
self.resolveBlock(response);
|
|
139
|
+
self.resolveBlock = nil;
|
|
140
|
+
self.rejectBlock = nil;
|
|
141
|
+
}
|
|
142
|
+
}];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
- (void)documentCameraViewController:(VNDocumentCameraViewController *)controller didFailWithError:(NSError *)error API_AVAILABLE(ios(13.0))
|
|
146
|
+
{
|
|
147
|
+
[controller dismissViewControllerAnimated:YES completion:^{
|
|
148
|
+
if (self.rejectBlock) {
|
|
149
|
+
self.rejectBlock(@"SCAN_ERROR", error.localizedDescription, error);
|
|
150
|
+
self.rejectBlock = nil;
|
|
151
|
+
self.resolveBlock = nil;
|
|
152
|
+
}
|
|
153
|
+
}];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.DocumentScanner = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
const LINKING_ERROR = 'The package \'react-native-document-scanner\' doesn\'t seem to be linked. Make sure: \n\n' + _reactNative.Platform.select({
|
|
9
|
+
ios: "- You have run 'pod install'\n",
|
|
10
|
+
default: ''
|
|
11
|
+
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
12
|
+
const RNDocumentScanner = _reactNative.NativeModules.RNDocumentScanner ? _reactNative.NativeModules.RNDocumentScanner : new Proxy({}, {
|
|
13
|
+
get() {
|
|
14
|
+
throw new Error(LINKING_ERROR);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Options for document scanning
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Response from document scanning
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Document Scanner module
|
|
28
|
+
*/
|
|
29
|
+
const DocumentScanner = exports.DocumentScanner = {
|
|
30
|
+
/**
|
|
31
|
+
* Start document scanning
|
|
32
|
+
*
|
|
33
|
+
* @param options - Optional configuration for the document scanner
|
|
34
|
+
* @returns Promise that resolves with scanned document data or rejects with error
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { DocumentScanner } from 'react-native-document-scanner';
|
|
39
|
+
*
|
|
40
|
+
* try {
|
|
41
|
+
* const result = await DocumentScanner.scanDocument({
|
|
42
|
+
* maxNumDocuments: 3,
|
|
43
|
+
* letUserAdjustCrop: true,
|
|
44
|
+
* croppedImageQuality: 100,
|
|
45
|
+
* responseType: 'base64'
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* if (result.status === 'success') {
|
|
49
|
+
* console.log('Scanned images:', result.scannedImages);
|
|
50
|
+
* } else {
|
|
51
|
+
* console.log('User cancelled');
|
|
52
|
+
* }
|
|
53
|
+
* } catch (error) {
|
|
54
|
+
* console.error('Scan error:', error);
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
scanDocument(options) {
|
|
59
|
+
const defaultOptions = {
|
|
60
|
+
maxNumDocuments: 1,
|
|
61
|
+
letUserAdjustCrop: true,
|
|
62
|
+
croppedImageQuality: 100,
|
|
63
|
+
responseType: 'imageFilePath'
|
|
64
|
+
};
|
|
65
|
+
const finalOptions = {
|
|
66
|
+
...defaultOptions,
|
|
67
|
+
...options
|
|
68
|
+
};
|
|
69
|
+
return RNDocumentScanner.scanDocument(finalOptions);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var _default = exports.default = DocumentScanner;
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","RNDocumentScanner","NativeModules","Proxy","get","Error","DocumentScanner","exports","scanDocument","options","defaultOptions","maxNumDocuments","letUserAdjustCrop","croppedImageQuality","responseType","finalOptions","_default"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GACjB,2FAA2F,GAC3FC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,iBAAiB,GAAGC,0BAAa,CAACD,iBAAiB,GACrDC,0BAAa,CAACD,iBAAiB,GAC/B,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACT,aAAa,CAAC;EAChC;AACF,CACF,CAAC;;AAEL;AACA;AACA;;AA6BA;AACA;AACA;;AAaA;AACA;AACA;AACO,MAAMU,eAAe,GAAAC,OAAA,CAAAD,eAAA,GAAG;EAC7B;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEE,YAAYA,CAACC,OAA6B,EAAiC;IACzE,MAAMC,cAAmC,GAAG;MAC1CC,eAAe,EAAE,CAAC;MAClBC,iBAAiB,EAAE,IAAI;MACvBC,mBAAmB,EAAE,GAAG;MACxBC,YAAY,EAAE;IAChB,CAAC;IAED,MAAMC,YAAY,GAAG;MAAE,GAAGL,cAAc;MAAE,GAAGD;IAAQ,CAAC;IAEtD,OAAOR,iBAAiB,CAACO,YAAY,CAACO,YAAY,CAAC;EACrD;AACF,CAAC;AAAC,IAAAC,QAAA,GAAAT,OAAA,CAAAP,OAAA,GAEaM,eAAe","ignoreList":[]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
const LINKING_ERROR = 'The package \'react-native-document-scanner\' doesn\'t seem to be linked. Make sure: \n\n' + Platform.select({
|
|
3
|
+
ios: "- You have run 'pod install'\n",
|
|
4
|
+
default: ''
|
|
5
|
+
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
6
|
+
const RNDocumentScanner = NativeModules.RNDocumentScanner ? NativeModules.RNDocumentScanner : new Proxy({}, {
|
|
7
|
+
get() {
|
|
8
|
+
throw new Error(LINKING_ERROR);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Options for document scanning
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Response from document scanning
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Document Scanner module
|
|
22
|
+
*/
|
|
23
|
+
export const DocumentScanner = {
|
|
24
|
+
/**
|
|
25
|
+
* Start document scanning
|
|
26
|
+
*
|
|
27
|
+
* @param options - Optional configuration for the document scanner
|
|
28
|
+
* @returns Promise that resolves with scanned document data or rejects with error
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { DocumentScanner } from 'react-native-document-scanner';
|
|
33
|
+
*
|
|
34
|
+
* try {
|
|
35
|
+
* const result = await DocumentScanner.scanDocument({
|
|
36
|
+
* maxNumDocuments: 3,
|
|
37
|
+
* letUserAdjustCrop: true,
|
|
38
|
+
* croppedImageQuality: 100,
|
|
39
|
+
* responseType: 'base64'
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* if (result.status === 'success') {
|
|
43
|
+
* console.log('Scanned images:', result.scannedImages);
|
|
44
|
+
* } else {
|
|
45
|
+
* console.log('User cancelled');
|
|
46
|
+
* }
|
|
47
|
+
* } catch (error) {
|
|
48
|
+
* console.error('Scan error:', error);
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
scanDocument(options) {
|
|
53
|
+
const defaultOptions = {
|
|
54
|
+
maxNumDocuments: 1,
|
|
55
|
+
letUserAdjustCrop: true,
|
|
56
|
+
croppedImageQuality: 100,
|
|
57
|
+
responseType: 'imageFilePath'
|
|
58
|
+
};
|
|
59
|
+
const finalOptions = {
|
|
60
|
+
...defaultOptions,
|
|
61
|
+
...options
|
|
62
|
+
};
|
|
63
|
+
return RNDocumentScanner.scanDocument(finalOptions);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
export default DocumentScanner;
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","RNDocumentScanner","Proxy","get","Error","DocumentScanner","scanDocument","options","defaultOptions","maxNumDocuments","letUserAdjustCrop","croppedImageQuality","responseType","finalOptions"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,MAAMC,aAAa,GACjB,2FAA2F,GAC3FD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,iBAAiB,GAAGN,aAAa,CAACM,iBAAiB,GACrDN,aAAa,CAACM,iBAAiB,GAC/B,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;;AAEL;AACA;AACA;;AA6BA;AACA;AACA;;AAaA;AACA;AACA;AACA,OAAO,MAAMQ,eAAe,GAAG;EAC7B;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEC,YAAYA,CAACC,OAA6B,EAAiC;IACzE,MAAMC,cAAmC,GAAG;MAC1CC,eAAe,EAAE,CAAC;MAClBC,iBAAiB,EAAE,IAAI;MACvBC,mBAAmB,EAAE,GAAG;MACxBC,YAAY,EAAE;IAChB,CAAC;IAED,MAAMC,YAAY,GAAG;MAAE,GAAGL,cAAc;MAAE,GAAGD;IAAQ,CAAC;IAEtD,OAAON,iBAAiB,CAACK,YAAY,CAACO,YAAY,CAAC;EACrD;AACF,CAAC;AAED,eAAeR,eAAe","ignoreList":[]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for document scanning
|
|
3
|
+
*/
|
|
4
|
+
export interface ScanDocumentOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Maximum number of documents to scan (Android only)
|
|
7
|
+
* @default 1
|
|
8
|
+
* @platform android
|
|
9
|
+
*/
|
|
10
|
+
maxNumDocuments?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Allow user to adjust crop manually (Android only)
|
|
13
|
+
* @default true
|
|
14
|
+
* @platform android
|
|
15
|
+
*/
|
|
16
|
+
letUserAdjustCrop?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Quality of the cropped image (0-100)
|
|
19
|
+
* @default 100
|
|
20
|
+
*/
|
|
21
|
+
croppedImageQuality?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Response type for scanned images
|
|
24
|
+
* @default 'imageFilePath'
|
|
25
|
+
*/
|
|
26
|
+
responseType?: 'base64' | 'imageFilePath';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Response from document scanning
|
|
30
|
+
*/
|
|
31
|
+
export interface ScanDocumentResponse {
|
|
32
|
+
/**
|
|
33
|
+
* Array of scanned images (base64 strings or file paths)
|
|
34
|
+
*/
|
|
35
|
+
scannedImages: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Status of the scan operation
|
|
38
|
+
*/
|
|
39
|
+
status: 'success' | 'cancel';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Document Scanner module
|
|
43
|
+
*/
|
|
44
|
+
export declare const DocumentScanner: {
|
|
45
|
+
/**
|
|
46
|
+
* Start document scanning
|
|
47
|
+
*
|
|
48
|
+
* @param options - Optional configuration for the document scanner
|
|
49
|
+
* @returns Promise that resolves with scanned document data or rejects with error
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { DocumentScanner } from 'react-native-document-scanner';
|
|
54
|
+
*
|
|
55
|
+
* try {
|
|
56
|
+
* const result = await DocumentScanner.scanDocument({
|
|
57
|
+
* maxNumDocuments: 3,
|
|
58
|
+
* letUserAdjustCrop: true,
|
|
59
|
+
* croppedImageQuality: 100,
|
|
60
|
+
* responseType: 'base64'
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* if (result.status === 'success') {
|
|
64
|
+
* console.log('Scanned images:', result.scannedImages);
|
|
65
|
+
* } else {
|
|
66
|
+
* console.log('User cancelled');
|
|
67
|
+
* }
|
|
68
|
+
* } catch (error) {
|
|
69
|
+
* console.error('Scan error:', error);
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
scanDocument(options?: ScanDocumentOptions): Promise<ScanDocumentResponse>;
|
|
74
|
+
};
|
|
75
|
+
export default DocumentScanner;
|
|
76
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAmBA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;OAGG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,eAAe,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC9B;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;2BACoB,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAY3E,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rn-document-scanner-vision",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A React Native library for scanning documents on iOS and Android",
|
|
5
|
+
"main": "lib/commonjs/index.js",
|
|
6
|
+
"module": "lib/module/index.js",
|
|
7
|
+
"types": "lib/typescript/index.d.ts",
|
|
8
|
+
"react-native": "src/index.tsx",
|
|
9
|
+
"source": "src/index.tsx",
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"lib",
|
|
13
|
+
"android",
|
|
14
|
+
"ios",
|
|
15
|
+
"react-native-document-scanner.podspec",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"typescript": "tsc --noEmit",
|
|
21
|
+
"build": "bob build",
|
|
22
|
+
"prepare": "bob build",
|
|
23
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
24
|
+
"lint:fix": "eslint \"**/*.{js,ts,tsx}\" --fix"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"react-native",
|
|
28
|
+
"ios",
|
|
29
|
+
"android",
|
|
30
|
+
"document-scanner",
|
|
31
|
+
"scanner",
|
|
32
|
+
"ocr",
|
|
33
|
+
"document",
|
|
34
|
+
"scan",
|
|
35
|
+
"camera",
|
|
36
|
+
"visionkit",
|
|
37
|
+
"mlkit"
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/hoangthien123/react-native-document-scanner.git"
|
|
42
|
+
},
|
|
43
|
+
"author": "Hoang Thien <hoangthien123@example.com>",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/hoangthien123/react-native-document-scanner/issues"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/hoangthien123/react-native-document-scanner#readme",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"registry": "https://registry.npmjs.org/"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": "*",
|
|
54
|
+
"react-native": "*"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@react-native/eslint-config": "^0.74.0",
|
|
58
|
+
"@types/react": "^18.2.44",
|
|
59
|
+
"@types/react-native": "^0.72.8",
|
|
60
|
+
"eslint": "^8.19.0",
|
|
61
|
+
"prettier": "^3.0.0",
|
|
62
|
+
"react": "^18.2.0",
|
|
63
|
+
"react-native": "^0.74.0",
|
|
64
|
+
"react-native-builder-bob": "^0.23.2",
|
|
65
|
+
"typescript": "^5.3.3"
|
|
66
|
+
},
|
|
67
|
+
"react-native-builder-bob": {
|
|
68
|
+
"source": "src",
|
|
69
|
+
"output": "lib",
|
|
70
|
+
"targets": [
|
|
71
|
+
"commonjs",
|
|
72
|
+
"module",
|
|
73
|
+
[
|
|
74
|
+
"typescript",
|
|
75
|
+
{
|
|
76
|
+
"project": "tsconfig.build.json"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "react-native-document-scanner"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
s.platforms = { :ios => "13.0" }
|
|
13
|
+
s.source = { :git => package["repository"]["url"], :tag => "#{s.version}" }
|
|
14
|
+
|
|
15
|
+
s.source_files = "ios/**/*.{h,m,mm}"
|
|
16
|
+
|
|
17
|
+
s.dependency "React-Core"
|
|
18
|
+
|
|
19
|
+
# VisionKit is required for document scanning
|
|
20
|
+
s.frameworks = "VisionKit"
|
|
21
|
+
end
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
const LINKING_ERROR =
|
|
4
|
+
'The package \'react-native-document-scanner\' doesn\'t seem to be linked. Make sure: \n\n' +
|
|
5
|
+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
6
|
+
'- You rebuilt the app after installing the package\n' +
|
|
7
|
+
'- You are not using Expo Go\n';
|
|
8
|
+
|
|
9
|
+
const RNDocumentScanner = NativeModules.RNDocumentScanner
|
|
10
|
+
? NativeModules.RNDocumentScanner
|
|
11
|
+
: new Proxy(
|
|
12
|
+
{},
|
|
13
|
+
{
|
|
14
|
+
get() {
|
|
15
|
+
throw new Error(LINKING_ERROR);
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for document scanning
|
|
22
|
+
*/
|
|
23
|
+
export interface ScanDocumentOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Maximum number of documents to scan (Android only)
|
|
26
|
+
* @default 1
|
|
27
|
+
* @platform android
|
|
28
|
+
*/
|
|
29
|
+
maxNumDocuments?: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Allow user to adjust crop manually (Android only)
|
|
33
|
+
* @default true
|
|
34
|
+
* @platform android
|
|
35
|
+
*/
|
|
36
|
+
letUserAdjustCrop?: boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Quality of the cropped image (0-100)
|
|
40
|
+
* @default 100
|
|
41
|
+
*/
|
|
42
|
+
croppedImageQuality?: number;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Response type for scanned images
|
|
46
|
+
* @default 'imageFilePath'
|
|
47
|
+
*/
|
|
48
|
+
responseType?: 'base64' | 'imageFilePath';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Response from document scanning
|
|
53
|
+
*/
|
|
54
|
+
export interface ScanDocumentResponse {
|
|
55
|
+
/**
|
|
56
|
+
* Array of scanned images (base64 strings or file paths)
|
|
57
|
+
*/
|
|
58
|
+
scannedImages: string[];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Status of the scan operation
|
|
62
|
+
*/
|
|
63
|
+
status: 'success' | 'cancel';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Document Scanner module
|
|
68
|
+
*/
|
|
69
|
+
export const DocumentScanner = {
|
|
70
|
+
/**
|
|
71
|
+
* Start document scanning
|
|
72
|
+
*
|
|
73
|
+
* @param options - Optional configuration for the document scanner
|
|
74
|
+
* @returns Promise that resolves with scanned document data or rejects with error
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { DocumentScanner } from 'react-native-document-scanner';
|
|
79
|
+
*
|
|
80
|
+
* try {
|
|
81
|
+
* const result = await DocumentScanner.scanDocument({
|
|
82
|
+
* maxNumDocuments: 3,
|
|
83
|
+
* letUserAdjustCrop: true,
|
|
84
|
+
* croppedImageQuality: 100,
|
|
85
|
+
* responseType: 'base64'
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* if (result.status === 'success') {
|
|
89
|
+
* console.log('Scanned images:', result.scannedImages);
|
|
90
|
+
* } else {
|
|
91
|
+
* console.log('User cancelled');
|
|
92
|
+
* }
|
|
93
|
+
* } catch (error) {
|
|
94
|
+
* console.error('Scan error:', error);
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
scanDocument(options?: ScanDocumentOptions): Promise<ScanDocumentResponse> {
|
|
99
|
+
const defaultOptions: ScanDocumentOptions = {
|
|
100
|
+
maxNumDocuments: 1,
|
|
101
|
+
letUserAdjustCrop: true,
|
|
102
|
+
croppedImageQuality: 100,
|
|
103
|
+
responseType: 'imageFilePath',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
107
|
+
|
|
108
|
+
return RNDocumentScanner.scanDocument(finalOptions);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export default DocumentScanner;
|