react-native-rectangle-doc-scanner 15.3.0 → 15.5.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 CHANGED
@@ -1,8 +1,639 @@
1
1
  # React Native Document Scanner Wrapper
2
2
 
3
+ [English](#english-version) | [한국어](#한국어-버전)
4
+
5
+ ---
6
+
7
+ ## 한국어 버전
8
+
9
+ React Native용 문서 스캐너 라이브러리입니다. [`react-native-document-scanner`](https://github.com/Michaelvilleneuve/react-native-document-scanner)를 래핑하여 iOS와 Android 모두에서 네이티브 문서 스캐너를 제공합니다.
10
+
11
+ > 네이티브 구현은 업스트림 라이브러리에 포함되어 있습니다 (iOS: Objective-C/OpenCV, Android: Kotlin/OpenCV). 이 패키지는 타입 안전한 래퍼, 선택적 크롭 에디터 헬퍼, 전체 화면 스캐너를 제공합니다.
12
+
13
+ ## ✨ 전문가급 카메라 품질 (v3.2+)
14
+
15
+ **주요 업데이트:** 최신 `AVCapturePhotoOutput` API로 업그레이드되어 이미지 품질이 대폭 향상되었습니다!
16
+
17
+ ### 🚀 새로운 기능:
18
+ - **최신 카메라 API** - 구형 `AVCaptureStillImageOutput` 대신 `AVCapturePhotoOutput` (iOS 10+) 사용
19
+ - **iPhone 네이티브 품질** - 기본 카메라 앱과 동일한 품질
20
+ - **컴퓨테이셔널 포토그래피** - 자동 HDR, Deep Fusion, Smart HDR 지원
21
+ - **12MP+ 해상도** - 최신 iPhone에서 전체 해상도 캡처 (iPhone 14 Pro+ 기준 최대 48MP)
22
+ - **최대 품질 우선순위** - iOS 13+ 품질 우선순위 활성화
23
+ - **95%+ JPEG 품질** - 품질 손실 방지를 위한 최소 압축 품질 강제
24
+
25
+ ### 🎯 자동 최적화:
26
+ - **고해상도 캡처** - 전체 센서 해상도 활성화 (`AVCaptureSessionPresetHigh`)
27
+ - **최소 95% JPEG** - 압축으로 인한 품질 저하 방지
28
+ - **고급 기능**:
29
+ - 더 선명한 이미지를 위한 비디오 안정화
30
+ - 항상 선명한 캡처를 위한 연속 자동 초점
31
+ - 자동 노출 및 화이트 밸런스
32
+ - 어두운 환경에서 저조도 부스트
33
+ - **하드웨어 가속** - 효율적인 처리를 위한 CIContext
34
+
35
+ ### ⚡ 완전 자동 설치:
36
+ yarn/npm으로 설치하기만 하면 됩니다 - **수동 설정 불필요!**
37
+ - Postinstall 스크립트가 자동으로 카메라 품질 패치
38
+ - 설치 중 iOS 최적화 파일 자동 복사
39
+ - `pod install` 후 즉시 사용 가능
40
+
41
+ ## 빠른 시작 가이드
42
+
43
+ ```bash
44
+ # 1. 패키지 설치
45
+ yarn add react-native-rectangle-doc-scanner \
46
+ github:Michaelvilleneuve/react-native-document-scanner \
47
+ react-native-perspective-image-cropper \
48
+ react-native-fs \
49
+ react-native-image-crop-picker \
50
+ react-native-image-picker \
51
+ react-native-svg \
52
+ expo-modules-core
53
+
54
+ # 2. iOS 설정
55
+ cd ios && pod install && cd ..
56
+
57
+ # 3. iOS Info.plist에 카메라 권한 추가 (수동)
58
+ # 4. 앱 실행
59
+ npx react-native run-ios
60
+ # 또는
61
+ npx react-native run-android
62
+ ```
63
+
64
+ ## 설치 방법
65
+
66
+ ### 1. 패키지 설치
67
+
68
+ ```bash
69
+ yarn add react-native-rectangle-doc-scanner \
70
+ github:Michaelvilleneuve/react-native-document-scanner \
71
+ react-native-perspective-image-cropper
72
+ ```
73
+
74
+ 또는 npm 사용:
75
+
76
+ ```bash
77
+ npm install react-native-rectangle-doc-scanner \
78
+ github:Michaelvilleneuve/react-native-document-scanner \
79
+ react-native-perspective-image-cropper
80
+ ```
81
+
82
+ ### 2. Peer Dependencies 설치
83
+
84
+ 이 라이브러리는 다음 peer dependencies를 필요로 합니다:
85
+
86
+ ```bash
87
+ yarn add react-native-fs \
88
+ react-native-image-crop-picker \
89
+ react-native-image-picker \
90
+ react-native-svg \
91
+ expo-modules-core
92
+ ```
93
+
94
+ 또는 npm 사용:
95
+
96
+ ```bash
97
+ npm install react-native-fs \
98
+ react-native-image-crop-picker \
99
+ react-native-image-picker \
100
+ react-native-svg \
101
+ expo-modules-core
102
+ ```
103
+
104
+ **선택사항 (이미지 회전 기능을 사용하려면):**
105
+
106
+ ```bash
107
+ # 둘 중 하나 선택
108
+ yarn add expo-image-manipulator
109
+ # 또는
110
+ yarn add react-native-image-rotate
111
+ ```
112
+
113
+ ### 2-1. Babel 및 Reanimated 설정 (필요시)
114
+
115
+ 프로젝트에 `babel.config.js` 파일이 있는 경우, 다음 플러그인이 필요할 수 있습니다:
116
+
117
+ ```javascript
118
+ module.exports = {
119
+ presets: ['module:@react-native/babel-preset'],
120
+ plugins: [
121
+ 'react-native-reanimated/plugin' // 마지막에 위치해야 함
122
+ ],
123
+ };
124
+ ```
125
+
126
+ **필요한 경우 추가 패키지:**
127
+
128
+ ```bash
129
+ yarn add react-native-reanimated
130
+ ```
131
+
132
+ ### 3. iOS 설정
133
+
134
+ ```bash
135
+ cd ios && pod install && cd ..
136
+ ```
137
+
138
+ **Info.plist에 카메라 권한 추가:**
139
+
140
+ `ios/YourApp/Info.plist` 파일에 다음 권한을 추가하세요:
141
+
142
+ ```xml
143
+ <key>NSCameraUsageDescription</key>
144
+ <string>문서를 스캔하기 위해 카메라 접근이 필요합니다</string>
145
+ <key>NSPhotoLibraryUsageDescription</key>
146
+ <string>스캔한 문서를 저장하기 위해 사진 라이브러리 접근이 필요합니다</string>
147
+ <key>NSPhotoLibraryAddUsageDescription</key>
148
+ <string>스캔한 문서를 저장하기 위해 사진 라이브러리 접근이 필요합니다</string>
149
+ ```
150
+
151
+ ### 4. Android 설정
152
+
153
+ Android는 자동으로 네이티브 모듈을 링크합니다. 레거시 아키텍처를 사용하는 경우, `MainApplication.java`에서 `DocumentScannerPackage()`를 수동으로 등록해야 합니다.
154
+
155
+ **AndroidManifest.xml에 권한 추가:**
156
+
157
+ `android/app/src/main/AndroidManifest.xml` 파일에 다음 권한이 자동으로 포함됩니다:
158
+
159
+ ```xml
160
+ <uses-permission android:name="android.permission.CAMERA" />
161
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
162
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
163
+
164
+ <uses-feature android:name="android.hardware.camera" android:required="true" />
165
+ <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
166
+ <uses-feature android:name="android.hardware.camera.flash" android:required="false" />
167
+ ```
168
+
169
+ **Gradle 설정:**
170
+
171
+ 라이브러리는 다음 최소 요구사항을 가지고 있습니다:
172
+ - `minSdkVersion`: 21
173
+ - `compileSdkVersion`: 33
174
+ - `targetSdkVersion`: 33
175
+ - Kotlin: 1.8.21
176
+ - Java: 17
177
+
178
+ 이 설정은 자동으로 적용되지만, 프로젝트의 `android/build.gradle`에서 호환되는 버전을 사용하는지 확인하세요.
179
+
180
+ **프로젝트의 `android/build.gradle` 예시:**
181
+
182
+ ```gradle
183
+ buildscript {
184
+ ext {
185
+ buildToolsVersion = "33.0.0"
186
+ minSdkVersion = 21
187
+ compileSdkVersion = 33
188
+ targetSdkVersion = 33
189
+ kotlinVersion = "1.8.21"
190
+ }
191
+ dependencies {
192
+ classpath("com.android.tools.build:gradle:7.4.2")
193
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
194
+ }
195
+ }
196
+ ```
197
+
198
+ **프로젝트의 `android/app/build.gradle` 예시:**
199
+
200
+ ```gradle
201
+ android {
202
+ compileSdkVersion rootProject.ext.compileSdkVersion
203
+
204
+ compileOptions {
205
+ sourceCompatibility JavaVersion.VERSION_17
206
+ targetCompatibility JavaVersion.VERSION_17
207
+ }
208
+
209
+ kotlinOptions {
210
+ jvmTarget = '17'
211
+ }
212
+
213
+ defaultConfig {
214
+ minSdkVersion rootProject.ext.minSdkVersion
215
+ targetSdkVersion rootProject.ext.targetSdkVersion
216
+ }
217
+ }
218
+ ```
219
+
220
+ ### 5. 자동 품질 패치 (Postinstall)
221
+
222
+ 이 라이브러리는 **postinstall 스크립트**를 통해 자동으로 카메라 품질을 최적화합니다:
223
+
224
+ ```bash
225
+ # 패키지 설치 시 자동 실행됨
226
+ node scripts/postinstall.js
227
+ ```
228
+
229
+ **postinstall이 하는 일:**
230
+ 1. `react-native-document-scanner` 패키지를 찾습니다 (node_modules에서 자동 감지)
231
+ 2. vendor 폴더의 최적화된 iOS 파일들을 복사합니다:
232
+ - `IPDFCameraViewController.m/h` - AVCapturePhotoOutput 사용
233
+ - `DocumentScannerView.m/h` - 고품질 설정
234
+ - `RNPdfScannerManager.m/h` - 네이티브 브릿지
235
+ - `ios.js`, `index.js` - JavaScript 인터페이스
236
+ 3. 원본 파일은 `.original` 확장자로 백업됩니다
237
+
238
+ **수동으로 실행하려면:**
239
+
240
+ ```bash
241
+ npm run postinstall
242
+ # 또는
243
+ node scripts/postinstall.js
244
+ ```
245
+
246
+ **문제 해결:**
247
+ - postinstall이 실패하는 경우, `react-native-document-scanner`가 설치되어 있는지 확인하세요
248
+ - yarn workspaces나 monorepo를 사용하는 경우, 패키지 호이스팅으로 인해 경로가 다를 수 있습니다
249
+
250
+ ### 6. 런타임 권한 요청
251
+
252
+ 앱에서 런타임에 카메라 권한을 요청해야 합니다:
253
+
254
+ ```typescript
255
+ import { PermissionsAndroid, Platform } from 'react-native';
256
+
257
+ async function requestCameraPermission() {
258
+ if (Platform.OS === 'android') {
259
+ try {
260
+ const granted = await PermissionsAndroid.request(
261
+ PermissionsAndroid.PERMISSIONS.CAMERA,
262
+ {
263
+ title: '카메라 권한',
264
+ message: '문서를 스캔하기 위해 카메라 접근이 필요합니다',
265
+ buttonNeutral: '나중에',
266
+ buttonNegative: '거부',
267
+ buttonPositive: '허용',
268
+ }
269
+ );
270
+ return granted === PermissionsAndroid.RESULTS.GRANTED;
271
+ } catch (err) {
272
+ console.warn(err);
273
+ return false;
274
+ }
275
+ }
276
+ return true;
277
+ }
278
+ ```
279
+
280
+ ## 사용 방법
281
+
282
+ ### 기본 사용 예제
283
+
284
+ ```tsx
285
+ import React, { useRef } from 'react';
286
+ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
287
+ import { DocScanner, type DocScannerHandle } from 'react-native-rectangle-doc-scanner';
288
+
289
+ export const ScanScreen = () => {
290
+ const scannerRef = useRef<DocScannerHandle>(null);
291
+
292
+ return (
293
+ <View style={styles.container}>
294
+ <DocScanner
295
+ ref={scannerRef}
296
+ overlayColor="rgba(0, 126, 244, 0.35)"
297
+ autoCapture
298
+ minStableFrames={6}
299
+ onCapture={(result) => {
300
+ console.log('문서 캡처됨:', result.path);
301
+ console.log('크기:', result.width, 'x', result.height);
302
+ }}
303
+ >
304
+ <View style={styles.overlay} pointerEvents="none">
305
+ <Text style={styles.hint}>프레임 안에 문서를 정렬하세요</Text>
306
+ </View>
307
+ </DocScanner>
308
+
309
+ <TouchableOpacity
310
+ style={styles.captureButton}
311
+ onPress={() => scannerRef.current?.capture()}
312
+ >
313
+ <Text style={styles.captureButtonText}>촬영</Text>
314
+ </TouchableOpacity>
315
+ </View>
316
+ );
317
+ };
318
+
319
+ const styles = StyleSheet.create({
320
+ container: {
321
+ flex: 1,
322
+ backgroundColor: '#000'
323
+ },
324
+ overlay: {
325
+ position: 'absolute',
326
+ top: 60,
327
+ alignSelf: 'center',
328
+ paddingHorizontal: 20,
329
+ paddingVertical: 10,
330
+ borderRadius: 12,
331
+ backgroundColor: 'rgba(0,0,0,0.5)',
332
+ },
333
+ hint: {
334
+ color: '#fff',
335
+ fontWeight: '600'
336
+ },
337
+ captureButton: {
338
+ position: 'absolute',
339
+ bottom: 40,
340
+ alignSelf: 'center',
341
+ width: 70,
342
+ height: 70,
343
+ borderRadius: 35,
344
+ backgroundColor: '#fff',
345
+ justifyContent: 'center',
346
+ alignItems: 'center',
347
+ },
348
+ captureButtonText: {
349
+ color: '#000',
350
+ fontWeight: '600',
351
+ },
352
+ });
353
+ ```
354
+
355
+ ## Props
356
+
357
+ `<DocScanner />` 컴포넌트는 다음 props를 지원합니다:
358
+
359
+ | Prop | 타입 | 기본값 | 설명 |
360
+ | --- | --- | --- | --- |
361
+ | `overlayColor` | `string` | `#0b7ef4` | 네이티브 오버레이 색상 |
362
+ | `autoCapture` | `boolean` | `true` | 자동 캡처 활성화 (내부적으로 `manualOnly`로 매핑됨) |
363
+ | `minStableFrames` | `number` | `8` | 자동 캡처 전 필요한 안정적인 프레임 수 |
364
+ | `enableTorch` | `boolean` | `false` | 플래시 켜기/끄기 |
365
+ | `quality` | `number` | `90` | 이미지 품질 (0–100, 네이티브용으로 변환됨) |
366
+ | `useBase64` | `boolean` | `false` | 파일 URI 대신 base64로 반환 |
367
+ | `onCapture` | `(result) => void` | — | `{ path, quad: null, width, height }` 객체를 전달받음 |
368
+
369
+ ### 수동 캡처
370
+
371
+ ref를 통해 `capture()` 메서드를 사용하여 수동으로 캡처할 수 있습니다. children을 사용하여 카메라 프리뷰 위에 커스텀 UI(버튼, 진행 표시기, 온보딩 팁 등)를 렌더링할 수 있습니다.
372
+
373
+ ## 추가 API
374
+
375
+ ### CropEditor
376
+
377
+ `react-native-perspective-image-cropper`를 래핑하여 수동으로 모서리를 조정할 수 있는 크롭 에디터를 제공합니다.
378
+
379
+ ```tsx
380
+ import { CropEditor } from 'react-native-rectangle-doc-scanner';
381
+
382
+ <CropEditor
383
+ imagePath={capturedImagePath}
384
+ onCropComplete={(croppedPath) => {
385
+ console.log('크롭된 이미지:', croppedPath);
386
+ }}
387
+ onCancel={() => {
388
+ console.log('크롭 취소');
389
+ }}
390
+ />
391
+ ```
392
+
393
+ ### FullDocScanner
394
+
395
+ 스캐너와 크롭 에디터를 단일 모달형 플로우로 제공합니다. `expo-image-manipulator` 또는 `react-native-image-rotate`가 설치되어 있으면, 확인 화면에서 90° 회전 버튼이 표시됩니다.
396
+
397
+ ```tsx
398
+ import { FullDocScanner } from 'react-native-rectangle-doc-scanner';
399
+
400
+ <FullDocScanner
401
+ onComplete={(result) => {
402
+ console.log('완료:', result);
403
+ }}
404
+ onCancel={() => {
405
+ console.log('취소');
406
+ }}
407
+ />
408
+ ```
409
+
410
+ ## 의존성 패키지 상세 정보
411
+
412
+ 이 라이브러리는 다양한 패키지에 의존합니다. 각 패키지의 역할은 다음과 같습니다:
413
+
414
+ ### 필수 의존성 (Peer Dependencies)
415
+
416
+ | 패키지 | 역할 | 필수 여부 |
417
+ |--------|------|-----------|
418
+ | `react-native-fs` | 파일 시스템 접근 (이미지 저장/읽기) | ✅ 필수 |
419
+ | `react-native-image-crop-picker` | 이미지 선택 및 크롭 | ✅ 필수 |
420
+ | `react-native-image-picker` | 갤러리/카메라에서 이미지 선택 | ✅ 필수 |
421
+ | `react-native-svg` | SVG 렌더링 (UI 오버레이) | ✅ 필수 |
422
+ | `expo-modules-core` | Expo 모듈 코어 기능 | ✅ 필수 |
423
+ | `expo-image-manipulator` | 이미지 회전 및 편집 | ⚙️ 선택 (회전 기능용) |
424
+ | `react-native-image-rotate` | 이미지 회전 (대안) | ⚙️ 선택 (회전 기능용) |
425
+
426
+ ### 내부 의존성 (Dependencies)
427
+
428
+ | 패키지 | 역할 |
429
+ |--------|------|
430
+ | `react-native-document-scanner` | 네이티브 문서 스캐너 구현 (GitHub) |
431
+ | `react-native-perspective-image-cropper` | 원근 보정 크롭 에디터 |
432
+ | `prop-types` | React PropTypes 검증 |
433
+
434
+ ### 개발 의존성 (DevDependencies)
435
+
436
+ | 패키지 | 역할 |
437
+ |--------|------|
438
+ | `typescript` | TypeScript 컴파일러 |
439
+ | `@types/react` | React 타입 정의 |
440
+ | `@types/react-native` | React Native 타입 정의 |
441
+ | `@types/react-native-fs` | react-native-fs 타입 정의 |
442
+
443
+ ### 네이티브 의존성
444
+
445
+ **iOS (CocoaPods):**
446
+ - OpenCV (이미지 처리 및 문서 감지)
447
+ - AVFoundation (카메라 API)
448
+ - CoreImage (이미지 필터 및 품질 처리)
449
+
450
+ **Android (Gradle):**
451
+ - OpenCV 4.9.0 (문서 감지)
452
+ - CameraX 1.3.0 (카메라 API)
453
+ - Kotlin Coroutines 1.7.3 (비동기 처리)
454
+ - ML Kit Document Scanner (문서 스캔)
455
+ - ML Kit Object Detection (실시간 사각형 감지)
456
+ - AndroidX Core, AppCompat (Android 기본 라이브러리)
457
+
458
+ ## 기술 스택
459
+
460
+ ### iOS
461
+ - **언어**: Objective-C
462
+ - **카메라 API**: AVCapturePhotoOutput (iOS 10+)
463
+ - **이미지 처리**: OpenCV, CoreImage (CIContext)
464
+ - **최소 버전**: iOS 11.0
465
+ - **지원 아키텍처**: arm64, x86_64 (시뮬레이터)
466
+
467
+ ### Android
468
+ - **언어**: Kotlin 1.8.21
469
+ - **카메라**: CameraX 1.3.0, Camera2 API
470
+ - **이미지 처리**: OpenCV 4.9.0
471
+ - **ML Kit**: 문서 스캔 및 객체 감지
472
+ - **최소 SDK**: 21 (Android 5.0 Lollipop)
473
+ - **타겟 SDK**: 33 (Android 13 Tiramisu)
474
+ - **Java**: JDK 17
475
+ - **Gradle**: 7.4.2+
476
+ - **Android Gradle Plugin**: 7.4.2+
477
+
478
+ ## 문제 해결
479
+
480
+ ### iOS 빌드 오류
481
+
482
+ **Pod 설치 후에도 빌드 오류가 발생하는 경우:**
483
+
484
+ ```bash
485
+ cd ios
486
+ rm -rf Pods Podfile.lock
487
+ pod cache clean --all
488
+ pod install
489
+ cd ..
490
+ ```
491
+
492
+ **"Module not found" 또는 헤더 파일 관련 오류:**
493
+
494
+ ```bash
495
+ # Xcode에서 Product > Clean Build Folder (Shift + Cmd + K)
496
+ # 또는 터미널에서:
497
+ cd ios
498
+ xcodebuild clean -workspace YourApp.xcworkspace -scheme YourApp
499
+ cd ..
500
+ ```
501
+
502
+ **CocoaPods 버전 문제:**
503
+
504
+ ```bash
505
+ sudo gem install cocoapods
506
+ pod --version # 1.11.0 이상 권장
507
+ ```
508
+
509
+ ### Android 빌드 오류
510
+
511
+ **Gradle 빌드 오류가 발생하는 경우:**
512
+
513
+ ```bash
514
+ cd android
515
+ ./gradlew clean
516
+ ./gradlew --stop # Gradle daemon 중지
517
+ cd ..
518
+ ```
519
+
520
+ **Java 버전 오류:**
521
+
522
+ 이 라이브러리는 Java 17이 필요합니다. Java 버전을 확인하세요:
523
+
524
+ ```bash
525
+ java -version # java version "17.x.x" 확인
526
+ ```
527
+
528
+ **Kotlin 버전 충돌:**
529
+
530
+ `android/build.gradle`에서 Kotlin 버전이 1.8.21 이상인지 확인:
531
+
532
+ ```gradle
533
+ buildscript {
534
+ ext.kotlin_version = '1.8.21'
535
+ }
536
+ ```
537
+
538
+ **OpenCV 의존성 오류:**
539
+
540
+ OpenCV가 자동으로 다운로드되지 않는 경우:
541
+
542
+ ```bash
543
+ cd android
544
+ ./gradlew clean
545
+ ./gradlew :app:dependencies # 의존성 확인
546
+ cd ..
547
+ ```
548
+
549
+ ### 권한 오류
550
+
551
+ **카메라가 작동하지 않는 경우:**
552
+
553
+ 1. **iOS**: Info.plist에 권한 설명이 추가되어 있는지 확인:
554
+ - `NSCameraUsageDescription`
555
+ - `NSPhotoLibraryUsageDescription`
556
+ - `NSPhotoLibraryAddUsageDescription`
557
+
558
+ 2. **Android**: PermissionsAndroid로 런타임 권한 요청:
559
+ ```typescript
560
+ await PermissionsAndroid.request(
561
+ PermissionsAndroid.PERMISSIONS.CAMERA
562
+ );
563
+ ```
564
+
565
+ 3. 기기 설정에서 앱의 카메라 권한이 허용되어 있는지 확인
566
+
567
+ ### Postinstall 스크립트 오류
568
+
569
+ **postinstall이 실행되지 않는 경우:**
570
+
571
+ ```bash
572
+ # 수동으로 postinstall 실행
573
+ node node_modules/react-native-rectangle-doc-scanner/scripts/postinstall.js
574
+
575
+ # 또는 패키지 재설치
576
+ rm -rf node_modules
577
+ yarn install # 또는 npm install
578
+ ```
579
+
580
+ **"react-native-document-scanner not found" 오류:**
581
+
582
+ ```bash
583
+ # react-native-document-scanner 설치 확인
584
+ yarn add github:Michaelvilleneuve/react-native-document-scanner
585
+ ```
586
+
587
+ ### Metro Bundler 오류
588
+
589
+ **"Unable to resolve module" 오류:**
590
+
591
+ ```bash
592
+ # Metro 캐시 삭제
593
+ npx react-native start --reset-cache
594
+
595
+ # 또는
596
+ rm -rf $TMPDIR/metro-*
597
+ rm -rf $TMPDIR/haste-*
598
+ ```
599
+
600
+ ### Peer Dependencies 경고
601
+
602
+ **"unmet peer dependency" 경고가 나타나는 경우:**
603
+
604
+ 모든 peer dependencies를 설치했는지 확인:
605
+
606
+ ```bash
607
+ yarn add react-native-fs \
608
+ react-native-image-crop-picker \
609
+ react-native-image-picker \
610
+ react-native-svg \
611
+ expo-modules-core
612
+ ```
613
+
614
+ ### Expo 프로젝트
615
+
616
+ Expo를 사용하는 경우, 일부 네이티브 모듈이 Expo Go에서 작동하지 않을 수 있습니다.
617
+ 개발 빌드(development build)를 사용하세요:
618
+
619
+ ```bash
620
+ npx expo prebuild
621
+ npx expo run:ios
622
+ # 또는
623
+ npx expo run:android
624
+ ```
625
+
626
+ ## 라이선스
627
+
628
+ MIT
629
+
630
+ ---
631
+
632
+ ## English Version
633
+
3
634
  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.
4
635
 
5
- > The native implementation lives inside the upstream library (ObjectiveC/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.
636
+ > 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.
6
637
 
7
638
  ## ✨ Professional Camera Quality (v3.2+)
8
639
 
@@ -32,21 +663,249 @@ Just install with yarn/npm - **no manual configuration needed!**
32
663
  - Optimized iOS files copied during installation
33
664
  - Works immediately after `pod install`
34
665
 
666
+ ## Quick Start Guide
667
+
668
+ ```bash
669
+ # 1. Install packages
670
+ yarn add react-native-rectangle-doc-scanner \
671
+ github:Michaelvilleneuve/react-native-document-scanner \
672
+ react-native-perspective-image-cropper \
673
+ react-native-fs \
674
+ react-native-image-crop-picker \
675
+ react-native-image-picker \
676
+ react-native-svg \
677
+ expo-modules-core
678
+
679
+ # 2. iOS setup
680
+ cd ios && pod install && cd ..
681
+
682
+ # 3. Add camera permissions to iOS Info.plist (manual)
683
+ # 4. Run your app
684
+ npx react-native run-ios
685
+ # or
686
+ npx react-native run-android
687
+ ```
688
+
35
689
  ## Installation
36
690
 
691
+ ### 1. Install the Package
692
+
37
693
  ```bash
38
694
  yarn add react-native-rectangle-doc-scanner \
39
695
  github:Michaelvilleneuve/react-native-document-scanner \
40
696
  react-native-perspective-image-cropper
697
+ ```
698
+
699
+ Or using npm:
700
+
701
+ ```bash
702
+ npm install react-native-rectangle-doc-scanner \
703
+ github:Michaelvilleneuve/react-native-document-scanner \
704
+ react-native-perspective-image-cropper
705
+ ```
706
+
707
+ ### 2. Install Peer Dependencies
708
+
709
+ This library requires the following peer dependencies:
710
+
711
+ ```bash
712
+ yarn add react-native-fs \
713
+ react-native-image-crop-picker \
714
+ react-native-image-picker \
715
+ react-native-svg \
716
+ expo-modules-core
717
+ ```
718
+
719
+ Or using npm:
720
+
721
+ ```bash
722
+ npm install react-native-fs \
723
+ react-native-image-crop-picker \
724
+ react-native-image-picker \
725
+ react-native-svg \
726
+ expo-modules-core
727
+ ```
728
+
729
+ **Optional (for image rotation features):**
730
+
731
+ ```bash
732
+ # Choose one
733
+ yarn add expo-image-manipulator
734
+ # or
735
+ yarn add react-native-image-rotate
736
+ ```
737
+
738
+ ### 2-1. Babel and Reanimated Setup (if needed)
739
+
740
+ If your project has a `babel.config.js` file, you may need the following plugins:
741
+
742
+ ```javascript
743
+ module.exports = {
744
+ presets: ['module:@react-native/babel-preset'],
745
+ plugins: [
746
+ 'react-native-reanimated/plugin' // Must be listed last
747
+ ],
748
+ };
749
+ ```
750
+
751
+ **Install additional packages if needed:**
752
+
753
+ ```bash
754
+ yarn add react-native-reanimated
755
+ ```
756
+
757
+ ### 3. iOS Setup
758
+
759
+ ```bash
760
+ cd ios && pod install && cd ..
761
+ ```
762
+
763
+ **Add Camera Permissions to Info.plist:**
764
+
765
+ Add the following permissions to your `ios/YourApp/Info.plist` file:
766
+
767
+ ```xml
768
+ <key>NSCameraUsageDescription</key>
769
+ <string>We need camera access to scan documents</string>
770
+ <key>NSPhotoLibraryUsageDescription</key>
771
+ <string>We need photo library access to save scanned documents</string>
772
+ <key>NSPhotoLibraryAddUsageDescription</key>
773
+ <string>We need photo library access to save scanned documents</string>
774
+ ```
775
+
776
+ ### 4. Android Setup
777
+
778
+ Android automatically links the native module. If you manage packages manually (legacy architecture), register `DocumentScannerPackage()` in your `MainApplication.java`.
779
+
780
+ **Permissions are automatically included:**
781
+
782
+ The following permissions are automatically included in the library's `AndroidManifest.xml`:
783
+
784
+ ```xml
785
+ <uses-permission android:name="android.permission.CAMERA" />
786
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
787
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
788
+
789
+ <uses-feature android:name="android.hardware.camera" android:required="true" />
790
+ <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
791
+ <uses-feature android:name="android.hardware.camera.flash" android:required="false" />
792
+ ```
793
+
794
+ **Gradle Configuration:**
795
+
796
+ The library has the following minimum requirements:
797
+ - `minSdkVersion`: 21
798
+ - `compileSdkVersion`: 33
799
+ - `targetSdkVersion`: 33
800
+ - Kotlin: 1.8.21
801
+ - Java: 17
802
+
803
+ These are automatically applied, but make sure your project's `android/build.gradle` uses compatible versions.
804
+
805
+ **Example `android/build.gradle` configuration:**
806
+
807
+ ```gradle
808
+ buildscript {
809
+ ext {
810
+ buildToolsVersion = "33.0.0"
811
+ minSdkVersion = 21
812
+ compileSdkVersion = 33
813
+ targetSdkVersion = 33
814
+ kotlinVersion = "1.8.21"
815
+ }
816
+ dependencies {
817
+ classpath("com.android.tools.build:gradle:7.4.2")
818
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
819
+ }
820
+ }
821
+ ```
822
+
823
+ **Example `android/app/build.gradle` configuration:**
824
+
825
+ ```gradle
826
+ android {
827
+ compileSdkVersion rootProject.ext.compileSdkVersion
828
+
829
+ compileOptions {
830
+ sourceCompatibility JavaVersion.VERSION_17
831
+ targetCompatibility JavaVersion.VERSION_17
832
+ }
833
+
834
+ kotlinOptions {
835
+ jvmTarget = '17'
836
+ }
837
+
838
+ defaultConfig {
839
+ minSdkVersion rootProject.ext.minSdkVersion
840
+ targetSdkVersion rootProject.ext.targetSdkVersion
841
+ }
842
+ }
843
+ ```
844
+
845
+ ### 5. Automatic Quality Patch (Postinstall)
846
+
847
+ This library automatically optimizes camera quality through a **postinstall script**:
41
848
 
42
- # iOS
43
- cd ios && pod install
849
+ ```bash
850
+ # Automatically runs on package installation
851
+ node scripts/postinstall.js
44
852
  ```
45
853
 
46
- Android automatically links the native module. If you manage packages manually (legacy architecture), register `DocumentScannerPackage()` in your `MainApplication`.
854
+ **What postinstall does:**
855
+ 1. Locates the `react-native-document-scanner` package (auto-detected in node_modules)
856
+ 2. Copies optimized iOS files from the vendor folder:
857
+ - `IPDFCameraViewController.m/h` - Uses AVCapturePhotoOutput
858
+ - `DocumentScannerView.m/h` - High quality settings
859
+ - `RNPdfScannerManager.m/h` - Native bridge
860
+ - `ios.js`, `index.js` - JavaScript interface
861
+ 3. Original files are backed up with `.original` extension
862
+
863
+ **To run manually:**
864
+
865
+ ```bash
866
+ npm run postinstall
867
+ # or
868
+ node scripts/postinstall.js
869
+ ```
870
+
871
+ **Troubleshooting:**
872
+ - If postinstall fails, ensure `react-native-document-scanner` is installed
873
+ - When using yarn workspaces or monorepos, package hoisting may affect the path
874
+
875
+ ### 6. Request Runtime Permissions
876
+
877
+ You need to request camera permissions at runtime in your app:
878
+
879
+ ```typescript
880
+ import { PermissionsAndroid, Platform } from 'react-native';
881
+
882
+ async function requestCameraPermission() {
883
+ if (Platform.OS === 'android') {
884
+ try {
885
+ const granted = await PermissionsAndroid.request(
886
+ PermissionsAndroid.PERMISSIONS.CAMERA,
887
+ {
888
+ title: 'Camera Permission',
889
+ message: 'We need camera access to scan documents',
890
+ buttonNeutral: 'Ask Me Later',
891
+ buttonNegative: 'Cancel',
892
+ buttonPositive: 'OK',
893
+ }
894
+ );
895
+ return granted === PermissionsAndroid.RESULTS.GRANTED;
896
+ } catch (err) {
897
+ console.warn(err);
898
+ return false;
899
+ }
900
+ }
901
+ return true;
902
+ }
903
+ ```
47
904
 
48
905
  ## Usage
49
906
 
907
+ ### Basic Example
908
+
50
909
  ```tsx
51
910
  import React, { useRef } from 'react';
52
911
  import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
@@ -64,6 +923,7 @@ export const ScanScreen = () => {
64
923
  minStableFrames={6}
65
924
  onCapture={(result) => {
66
925
  console.log('Captured document:', result.path);
926
+ console.log('Dimensions:', result.width, 'x', result.height);
67
927
  }}
68
928
  >
69
929
  <View style={styles.overlay} pointerEvents="none">
@@ -74,13 +934,18 @@ export const ScanScreen = () => {
74
934
  <TouchableOpacity
75
935
  style={styles.captureButton}
76
936
  onPress={() => scannerRef.current?.capture()}
77
- />
937
+ >
938
+ <Text style={styles.captureButtonText}>Capture</Text>
939
+ </TouchableOpacity>
78
940
  </View>
79
941
  );
80
942
  };
81
943
 
82
944
  const styles = StyleSheet.create({
83
- container: { flex: 1, backgroundColor: '#000' },
945
+ container: {
946
+ flex: 1,
947
+ backgroundColor: '#000'
948
+ },
84
949
  overlay: {
85
950
  position: 'absolute',
86
951
  top: 60,
@@ -90,7 +955,10 @@ const styles = StyleSheet.create({
90
955
  borderRadius: 12,
91
956
  backgroundColor: 'rgba(0,0,0,0.5)',
92
957
  },
93
- hint: { color: '#fff', fontWeight: '600' },
958
+ hint: {
959
+ color: '#fff',
960
+ fontWeight: '600'
961
+ },
94
962
  captureButton: {
95
963
  position: 'absolute',
96
964
  bottom: 40,
@@ -99,10 +967,18 @@ const styles = StyleSheet.create({
99
967
  height: 70,
100
968
  borderRadius: 35,
101
969
  backgroundColor: '#fff',
970
+ justifyContent: 'center',
971
+ alignItems: 'center',
972
+ },
973
+ captureButtonText: {
974
+ color: '#000',
975
+ fontWeight: '600',
102
976
  },
103
977
  });
104
978
  ```
105
979
 
980
+ ## Props
981
+
106
982
  `<DocScanner />` passes through the important upstream props:
107
983
 
108
984
  | Prop | Type | Default | Notes |
@@ -115,12 +991,262 @@ const styles = StyleSheet.create({
115
991
  | `useBase64` | `boolean` | `false` | Return base64 payloads instead of file URIs. |
116
992
  | `onCapture` | `(result) => void` | — | Receives `{ path, quad: null, width, height }`. |
117
993
 
994
+ ### Manual Capture
995
+
118
996
  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.
119
997
 
120
998
  ## Convenience APIs
121
999
 
122
- - `CropEditor` – wraps `react-native-perspective-image-cropper` for manual corner adjustment.
123
- - `FullDocScanner` – puts the scanner and crop editor into a single modal-like flow. If the host app links either `expo-image-manipulator` or `react-native-image-rotate`, the confirmation screen exposes 90° rotation buttons; otherwise rotation controls remain hidden.
1000
+ ### CropEditor
1001
+
1002
+ Wraps `react-native-perspective-image-cropper` for manual corner adjustment.
1003
+
1004
+ ```tsx
1005
+ import { CropEditor } from 'react-native-rectangle-doc-scanner';
1006
+
1007
+ <CropEditor
1008
+ imagePath={capturedImagePath}
1009
+ onCropComplete={(croppedPath) => {
1010
+ console.log('Cropped image:', croppedPath);
1011
+ }}
1012
+ onCancel={() => {
1013
+ console.log('Crop cancelled');
1014
+ }}
1015
+ />
1016
+ ```
1017
+
1018
+ ### FullDocScanner
1019
+
1020
+ Puts the scanner and crop editor into a single modal-like flow. If the host app links either `expo-image-manipulator` or `react-native-image-rotate`, the confirmation screen exposes 90° rotation buttons; otherwise rotation controls remain hidden.
1021
+
1022
+ ```tsx
1023
+ import { FullDocScanner } from 'react-native-rectangle-doc-scanner';
1024
+
1025
+ <FullDocScanner
1026
+ onComplete={(result) => {
1027
+ console.log('Completed:', result);
1028
+ }}
1029
+ onCancel={() => {
1030
+ console.log('Cancelled');
1031
+ }}
1032
+ />
1033
+ ```
1034
+
1035
+ ## Dependency Details
1036
+
1037
+ This library depends on various packages. Here's what each package does:
1038
+
1039
+ ### Required Dependencies (Peer Dependencies)
1040
+
1041
+ | Package | Purpose | Required |
1042
+ |---------|---------|----------|
1043
+ | `react-native-fs` | File system access (save/read images) | ✅ Required |
1044
+ | `react-native-image-crop-picker` | Image selection and cropping | ✅ Required |
1045
+ | `react-native-image-picker` | Pick images from gallery/camera | ✅ Required |
1046
+ | `react-native-svg` | SVG rendering (UI overlays) | ✅ Required |
1047
+ | `expo-modules-core` | Expo module core functionality | ✅ Required |
1048
+ | `expo-image-manipulator` | Image rotation and editing | ⚙️ Optional (for rotation) |
1049
+ | `react-native-image-rotate` | Image rotation (alternative) | ⚙️ Optional (for rotation) |
1050
+
1051
+ ### Internal Dependencies
1052
+
1053
+ | Package | Purpose |
1054
+ |---------|---------|
1055
+ | `react-native-document-scanner` | Native document scanner implementation (GitHub) |
1056
+ | `react-native-perspective-image-cropper` | Perspective correction crop editor |
1057
+ | `prop-types` | React PropTypes validation |
1058
+
1059
+ ### Development Dependencies
1060
+
1061
+ | Package | Purpose |
1062
+ |---------|---------|
1063
+ | `typescript` | TypeScript compiler |
1064
+ | `@types/react` | React type definitions |
1065
+ | `@types/react-native` | React Native type definitions |
1066
+ | `@types/react-native-fs` | react-native-fs type definitions |
1067
+
1068
+ ### Native Dependencies
1069
+
1070
+ **iOS (CocoaPods):**
1071
+ - OpenCV (image processing and document detection)
1072
+ - AVFoundation (camera API)
1073
+ - CoreImage (image filters and quality processing)
1074
+
1075
+ **Android (Gradle):**
1076
+ - OpenCV 4.9.0 (document detection)
1077
+ - CameraX 1.3.0 (camera API)
1078
+ - Kotlin Coroutines 1.7.3 (async processing)
1079
+ - ML Kit Document Scanner (document scanning)
1080
+ - ML Kit Object Detection (real-time rectangle detection)
1081
+ - AndroidX Core, AppCompat (Android base libraries)
1082
+
1083
+ ## Tech Stack
1084
+
1085
+ ### iOS
1086
+ - **Language**: Objective-C
1087
+ - **Camera API**: AVCapturePhotoOutput (iOS 10+)
1088
+ - **Image Processing**: OpenCV, CoreImage (CIContext)
1089
+ - **Minimum Version**: iOS 11.0
1090
+ - **Supported Architectures**: arm64, x86_64 (simulator)
1091
+
1092
+ ### Android
1093
+ - **Language**: Kotlin 1.8.21
1094
+ - **Camera**: CameraX 1.3.0, Camera2 API
1095
+ - **Image Processing**: OpenCV 4.9.0
1096
+ - **ML Kit**: Document scanning and object detection
1097
+ - **Minimum SDK**: 21 (Android 5.0 Lollipop)
1098
+ - **Target SDK**: 33 (Android 13 Tiramisu)
1099
+ - **Java**: JDK 17
1100
+ - **Gradle**: 7.4.2+
1101
+ - **Android Gradle Plugin**: 7.4.2+
1102
+
1103
+ ## Troubleshooting
1104
+
1105
+ ### iOS Build Errors
1106
+
1107
+ **If you encounter build errors after pod install:**
1108
+
1109
+ ```bash
1110
+ cd ios
1111
+ rm -rf Pods Podfile.lock
1112
+ pod cache clean --all
1113
+ pod install
1114
+ cd ..
1115
+ ```
1116
+
1117
+ **"Module not found" or header file related errors:**
1118
+
1119
+ ```bash
1120
+ # In Xcode: Product > Clean Build Folder (Shift + Cmd + K)
1121
+ # Or from terminal:
1122
+ cd ios
1123
+ xcodebuild clean -workspace YourApp.xcworkspace -scheme YourApp
1124
+ cd ..
1125
+ ```
1126
+
1127
+ **CocoaPods version issues:**
1128
+
1129
+ ```bash
1130
+ sudo gem install cocoapods
1131
+ pod --version # Recommended 1.11.0+
1132
+ ```
1133
+
1134
+ ### Android Build Errors
1135
+
1136
+ **If you encounter Gradle build errors:**
1137
+
1138
+ ```bash
1139
+ cd android
1140
+ ./gradlew clean
1141
+ ./gradlew --stop # Stop Gradle daemon
1142
+ cd ..
1143
+ ```
1144
+
1145
+ **Java version errors:**
1146
+
1147
+ This library requires Java 17. Check your Java version:
1148
+
1149
+ ```bash
1150
+ java -version # Should show "17.x.x"
1151
+ ```
1152
+
1153
+ **Kotlin version conflicts:**
1154
+
1155
+ Ensure Kotlin version in `android/build.gradle` is 1.8.21 or higher:
1156
+
1157
+ ```gradle
1158
+ buildscript {
1159
+ ext.kotlin_version = '1.8.21'
1160
+ }
1161
+ ```
1162
+
1163
+ **OpenCV dependency errors:**
1164
+
1165
+ If OpenCV doesn't download automatically:
1166
+
1167
+ ```bash
1168
+ cd android
1169
+ ./gradlew clean
1170
+ ./gradlew :app:dependencies # Check dependencies
1171
+ cd ..
1172
+ ```
1173
+
1174
+ ### Permission Errors
1175
+
1176
+ **If the camera is not working:**
1177
+
1178
+ 1. **iOS**: Check that permission descriptions are added to Info.plist:
1179
+ - `NSCameraUsageDescription`
1180
+ - `NSPhotoLibraryUsageDescription`
1181
+ - `NSPhotoLibraryAddUsageDescription`
1182
+
1183
+ 2. **Android**: Request runtime permissions using PermissionsAndroid:
1184
+ ```typescript
1185
+ await PermissionsAndroid.request(
1186
+ PermissionsAndroid.PERMISSIONS.CAMERA
1187
+ );
1188
+ ```
1189
+
1190
+ 3. Verify that camera permissions are granted in device settings
1191
+
1192
+ ### Postinstall Script Errors
1193
+
1194
+ **If postinstall doesn't run:**
1195
+
1196
+ ```bash
1197
+ # Run postinstall manually
1198
+ node node_modules/react-native-rectangle-doc-scanner/scripts/postinstall.js
1199
+
1200
+ # Or reinstall packages
1201
+ rm -rf node_modules
1202
+ yarn install # or npm install
1203
+ ```
1204
+
1205
+ **"react-native-document-scanner not found" error:**
1206
+
1207
+ ```bash
1208
+ # Verify react-native-document-scanner installation
1209
+ yarn add github:Michaelvilleneuve/react-native-document-scanner
1210
+ ```
1211
+
1212
+ ### Metro Bundler Errors
1213
+
1214
+ **"Unable to resolve module" error:**
1215
+
1216
+ ```bash
1217
+ # Clear Metro cache
1218
+ npx react-native start --reset-cache
1219
+
1220
+ # Or
1221
+ rm -rf $TMPDIR/metro-*
1222
+ rm -rf $TMPDIR/haste-*
1223
+ ```
1224
+
1225
+ ### Peer Dependencies Warning
1226
+
1227
+ **If you see "unmet peer dependency" warnings:**
1228
+
1229
+ Make sure all peer dependencies are installed:
1230
+
1231
+ ```bash
1232
+ yarn add react-native-fs \
1233
+ react-native-image-crop-picker \
1234
+ react-native-image-picker \
1235
+ react-native-svg \
1236
+ expo-modules-core
1237
+ ```
1238
+
1239
+ ### Expo Projects
1240
+
1241
+ If using Expo, some native modules may not work in Expo Go.
1242
+ Use a development build instead:
1243
+
1244
+ ```bash
1245
+ npx expo prebuild
1246
+ npx expo run:ios
1247
+ # or
1248
+ npx expo run:android
1249
+ ```
124
1250
 
125
1251
  ## License
126
1252
 
@@ -540,12 +540,14 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
540
540
  if (usesAndroidScannerActivity) {
541
541
  startAndroidScan().catch((error) => {
542
542
  const errorMessage = error instanceof Error ? error.message : String(error);
543
- console.error('[FullDocScanner] Android scan failed:', errorMessage, error);
544
- if (errorMessage.includes('SCAN_CANCELLED')) {
543
+ if (errorMessage.includes('SCAN_CANCELLED') || errorMessage.includes('Document scan cancelled')) {
545
544
  resetScannerView({ remount: true });
546
- onClose?.();
545
+ requestAnimationFrame(() => {
546
+ startAndroidScan().catch(() => null);
547
+ });
547
548
  return;
548
549
  }
550
+ console.error('[FullDocScanner] Android scan failed:', errorMessage, error);
549
551
  emitError(error instanceof Error ? error : new Error(String(error)), 'Failed to capture image. Please try again.');
550
552
  });
551
553
  return;
@@ -579,26 +581,30 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
579
581
  .catch((error) => {
580
582
  clearTimeout(captureTimeout);
581
583
  const errorMessage = error instanceof Error ? error.message : String(error);
582
- console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
583
584
  captureModeRef.current = null;
584
585
  captureInProgressRef.current = false;
585
- if (errorMessage.includes('SCAN_CANCELLED')) {
586
+ if (errorMessage.includes('SCAN_CANCELLED') || errorMessage.includes('Document scan cancelled')) {
586
587
  resetScannerView({ remount: true });
587
- onClose?.();
588
+ if (usesAndroidScannerActivity) {
589
+ requestAnimationFrame(() => {
590
+ startAndroidScan().catch(() => null);
591
+ });
592
+ }
588
593
  return;
589
594
  }
595
+ console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
590
596
  if (error instanceof Error && error.message !== 'capture_in_progress') {
591
597
  emitError(error, 'Failed to capture image. Please try again.');
592
598
  }
593
599
  });
594
600
  }, [
595
601
  emitError,
596
- onClose,
597
602
  processing,
598
603
  rectangleDetected,
599
604
  rectangleHint,
600
605
  captureReady,
601
606
  resetScannerView,
607
+ startAndroidScan,
602
608
  usesAndroidScannerActivity,
603
609
  ]);
604
610
  const handleGalleryPick = (0, react_1.useCallback)(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "15.3.0",
3
+ "version": "15.5.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -720,12 +720,14 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
720
720
  if (usesAndroidScannerActivity) {
721
721
  startAndroidScan().catch((error: unknown) => {
722
722
  const errorMessage = error instanceof Error ? error.message : String(error);
723
- console.error('[FullDocScanner] Android scan failed:', errorMessage, error);
724
- if (errorMessage.includes('SCAN_CANCELLED')) {
723
+ if (errorMessage.includes('SCAN_CANCELLED') || errorMessage.includes('Document scan cancelled')) {
725
724
  resetScannerView({ remount: true });
726
- onClose?.();
725
+ requestAnimationFrame(() => {
726
+ startAndroidScan().catch(() => null);
727
+ });
727
728
  return;
728
729
  }
730
+ console.error('[FullDocScanner] Android scan failed:', errorMessage, error);
729
731
  emitError(
730
732
  error instanceof Error ? error : new Error(String(error)),
731
733
  'Failed to capture image. Please try again.',
@@ -769,16 +771,20 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
769
771
  .catch((error: unknown) => {
770
772
  clearTimeout(captureTimeout);
771
773
  const errorMessage = error instanceof Error ? error.message : String(error);
772
- console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
773
774
  captureModeRef.current = null;
774
775
  captureInProgressRef.current = false;
775
776
 
776
- if (errorMessage.includes('SCAN_CANCELLED')) {
777
+ if (errorMessage.includes('SCAN_CANCELLED') || errorMessage.includes('Document scan cancelled')) {
777
778
  resetScannerView({ remount: true });
778
- onClose?.();
779
+ if (usesAndroidScannerActivity) {
780
+ requestAnimationFrame(() => {
781
+ startAndroidScan().catch(() => null);
782
+ });
783
+ }
779
784
  return;
780
785
  }
781
786
 
787
+ console.error('[FullDocScanner] Manual capture failed:', errorMessage, error);
782
788
  if (error instanceof Error && error.message !== 'capture_in_progress') {
783
789
  emitError(
784
790
  error,
@@ -788,12 +794,12 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
788
794
  });
789
795
  }, [
790
796
  emitError,
791
- onClose,
792
797
  processing,
793
798
  rectangleDetected,
794
799
  rectangleHint,
795
800
  captureReady,
796
801
  resetScannerView,
802
+ startAndroidScan,
797
803
  usesAndroidScannerActivity,
798
804
  ]);
799
805