react-native-rectangle-doc-scanner 3.98.0 → 3.100.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/dist/FullDocScanner.js +56 -52
- package/package.json +4 -5
- package/src/FullDocScanner.tsx +58 -61
- package/src/types/react-native-image-rotate.d.ts +0 -10
package/dist/FullDocScanner.js
CHANGED
|
@@ -41,7 +41,7 @@ const react_1 = __importStar(require("react"));
|
|
|
41
41
|
const react_native_1 = require("react-native");
|
|
42
42
|
const react_native_image_picker_1 = require("react-native-image-picker");
|
|
43
43
|
const react_native_image_crop_picker_1 = __importDefault(require("react-native-image-crop-picker"));
|
|
44
|
-
const
|
|
44
|
+
const react_native_fs_1 = __importDefault(require("react-native-fs"));
|
|
45
45
|
const DocScanner_1 = require("./DocScanner");
|
|
46
46
|
const stripFileUri = (value) => value.replace(/^file:\/\//, '');
|
|
47
47
|
const CROPPER_TIMEOUT_MS = 8000;
|
|
@@ -220,9 +220,22 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
220
220
|
}
|
|
221
221
|
if (normalizedDoc.croppedPath) {
|
|
222
222
|
console.log('[FullDocScanner] Grid detected: using pre-cropped image', normalizedDoc.croppedPath);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
// RNFS를 사용해서 base64 생성
|
|
224
|
+
try {
|
|
225
|
+
const base64Data = await react_native_fs_1.default.readFile(normalizedDoc.croppedPath, 'base64');
|
|
226
|
+
console.log('[FullDocScanner] Generated base64 for pre-cropped image, length:', base64Data.length);
|
|
227
|
+
setCroppedImageData({
|
|
228
|
+
path: normalizedDoc.croppedPath,
|
|
229
|
+
base64: base64Data,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.error('[FullDocScanner] Failed to generate base64:', error);
|
|
234
|
+
// base64 생성 실패 시 경로만 저장
|
|
235
|
+
setCroppedImageData({
|
|
236
|
+
path: normalizedDoc.croppedPath,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
226
239
|
return;
|
|
227
240
|
}
|
|
228
241
|
console.log('[FullDocScanner] Fallback to manual crop (no croppedPath available)');
|
|
@@ -330,61 +343,52 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
330
343
|
const handleFlashToggle = (0, react_1.useCallback)(() => {
|
|
331
344
|
setFlashEnabled(prev => !prev);
|
|
332
345
|
}, []);
|
|
333
|
-
const handleRotateImage = (0, react_1.useCallback)((degrees) => {
|
|
346
|
+
const handleRotateImage = (0, react_1.useCallback)(async (degrees) => {
|
|
334
347
|
if (!croppedImageData)
|
|
335
348
|
return;
|
|
336
|
-
// UI 회전 상태 먼저 업데이트 (즉각 반응)
|
|
337
|
-
setRotationDegrees(prev => {
|
|
338
|
-
const newRotation = (prev + degrees + 360) % 360;
|
|
339
|
-
return newRotation;
|
|
340
|
-
});
|
|
341
349
|
console.log('[FullDocScanner] Starting image rotation...', {
|
|
342
350
|
path: croppedImageData.path,
|
|
343
351
|
hasBase64: !!croppedImageData.base64,
|
|
352
|
+
base64Length: croppedImageData.base64?.length,
|
|
344
353
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
console.log('[FullDocScanner] Converted rotated image to base64, length:', base64Data?.length);
|
|
361
|
-
// 회전된 이미지로 교체 (base64 포함)
|
|
362
|
-
setCroppedImageData({
|
|
363
|
-
path: rotatedImageUri,
|
|
364
|
-
base64: base64Data,
|
|
365
|
-
});
|
|
366
|
-
// rotation degrees는 0으로 리셋
|
|
367
|
-
setRotationDegrees(0);
|
|
368
|
-
};
|
|
369
|
-
reader.readAsDataURL(blob);
|
|
370
|
-
}
|
|
371
|
-
catch (convertError) {
|
|
372
|
-
console.error('[FullDocScanner] Failed to convert to base64:', convertError);
|
|
373
|
-
// base64 변환 실패 시 URI만 저장
|
|
374
|
-
setCroppedImageData({
|
|
375
|
-
path: rotatedImageUri,
|
|
376
|
-
base64: undefined,
|
|
377
|
-
});
|
|
378
|
-
setRotationDegrees(0);
|
|
379
|
-
}
|
|
380
|
-
}, (error) => {
|
|
381
|
-
console.error('[FullDocScanner] Image rotation error:', error);
|
|
382
|
-
// 에러 발생 시 UI rotation 원복
|
|
383
|
-
setRotationDegrees(prev => {
|
|
384
|
-
const revertRotation = (prev - degrees + 360) % 360;
|
|
385
|
-
return revertRotation;
|
|
354
|
+
try {
|
|
355
|
+
// 원본 파일을 ImageCropPicker로 열어서 회전 적용
|
|
356
|
+
// 사용자가 수동으로 회전 버튼을 클릭하고 완료를 눌러야 함
|
|
357
|
+
const rotatedImage = await react_native_image_crop_picker_1.default.openCropper({
|
|
358
|
+
path: croppedImageData.path,
|
|
359
|
+
mediaType: 'photo',
|
|
360
|
+
cropping: true,
|
|
361
|
+
freeStyleCropEnabled: true,
|
|
362
|
+
includeBase64: true,
|
|
363
|
+
compressImageQuality: 0.9,
|
|
364
|
+
cropperToolbarTitle: degrees === 90 ? '우측 90° 회전 → 회전 버튼 클릭 → 완료' : '좌측 90° 회전 → 회전 버튼 클릭 → 완료',
|
|
365
|
+
cropperChooseText: '완료',
|
|
366
|
+
cropperCancelText: '취소',
|
|
367
|
+
cropperRotateButtonsHidden: false,
|
|
368
|
+
enableRotationGesture: true,
|
|
386
369
|
});
|
|
387
|
-
|
|
370
|
+
console.log('[FullDocScanner] Rotated image saved:', {
|
|
371
|
+
path: rotatedImage.path,
|
|
372
|
+
hasBase64: !!rotatedImage.data,
|
|
373
|
+
base64Length: rotatedImage.data?.length,
|
|
374
|
+
});
|
|
375
|
+
// 회전된 이미지로 교체
|
|
376
|
+
setCroppedImageData({
|
|
377
|
+
path: rotatedImage.path,
|
|
378
|
+
base64: rotatedImage.data ?? undefined,
|
|
379
|
+
});
|
|
380
|
+
// rotation degrees는 0으로 리셋
|
|
381
|
+
setRotationDegrees(0);
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
console.error('[FullDocScanner] Image rotation error:', error);
|
|
385
|
+
// 사용자가 취소했으면 아무것도 안함
|
|
386
|
+
const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : '';
|
|
387
|
+
if (!errorMessage.includes('cancel') && !errorMessage.includes('User cancelled')) {
|
|
388
|
+
// 에러 메시지 표시
|
|
389
|
+
react_native_1.Alert.alert('회전 실패', '이미지 회전 중 오류가 발생했습니다.');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
388
392
|
}, [croppedImageData]);
|
|
389
393
|
const handleConfirm = (0, react_1.useCallback)(() => {
|
|
390
394
|
if (!croppedImageData)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-rectangle-doc-scanner",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.100.0",
|
|
4
4
|
"description": "Native-backed document scanner for React Native with customizable overlays.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,19 +33,18 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"react": "*",
|
|
35
35
|
"react-native": "*",
|
|
36
|
+
"react-native-fs": "*",
|
|
36
37
|
"react-native-image-crop-picker": "*",
|
|
37
38
|
"react-native-image-picker": "*",
|
|
38
|
-
"react-native-image-rotate": "*",
|
|
39
39
|
"react-native-svg": "*"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/react": "^18.2.41",
|
|
43
43
|
"@types/react-native": "0.73.0",
|
|
44
|
+
"@types/react-native-fs": "^2.8.3",
|
|
44
45
|
"react-native-image-crop-picker": "^0.41.2",
|
|
45
46
|
"react-native-image-picker": "^7.1.2",
|
|
46
47
|
"typescript": "^5.3.3"
|
|
47
48
|
},
|
|
48
|
-
"dependencies": {
|
|
49
|
-
"react-native-image-rotate": "^2.1.0"
|
|
50
|
-
}
|
|
49
|
+
"dependencies": {}
|
|
51
50
|
}
|
package/src/FullDocScanner.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from 'react-native';
|
|
12
12
|
import { launchImageLibrary } from 'react-native-image-picker';
|
|
13
13
|
import ImageCropPicker from 'react-native-image-crop-picker';
|
|
14
|
-
import
|
|
14
|
+
import RNFS from 'react-native-fs';
|
|
15
15
|
import { DocScanner } from './DocScanner';
|
|
16
16
|
import type { CapturedDocument } from './types';
|
|
17
17
|
import type {
|
|
@@ -299,9 +299,23 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
299
299
|
|
|
300
300
|
if (normalizedDoc.croppedPath) {
|
|
301
301
|
console.log('[FullDocScanner] Grid detected: using pre-cropped image', normalizedDoc.croppedPath);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
302
|
+
|
|
303
|
+
// RNFS를 사용해서 base64 생성
|
|
304
|
+
try {
|
|
305
|
+
const base64Data = await RNFS.readFile(normalizedDoc.croppedPath, 'base64');
|
|
306
|
+
console.log('[FullDocScanner] Generated base64 for pre-cropped image, length:', base64Data.length);
|
|
307
|
+
|
|
308
|
+
setCroppedImageData({
|
|
309
|
+
path: normalizedDoc.croppedPath,
|
|
310
|
+
base64: base64Data,
|
|
311
|
+
});
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error('[FullDocScanner] Failed to generate base64:', error);
|
|
314
|
+
// base64 생성 실패 시 경로만 저장
|
|
315
|
+
setCroppedImageData({
|
|
316
|
+
path: normalizedDoc.croppedPath,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
305
319
|
return;
|
|
306
320
|
}
|
|
307
321
|
|
|
@@ -441,73 +455,56 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
441
455
|
setFlashEnabled(prev => !prev);
|
|
442
456
|
}, []);
|
|
443
457
|
|
|
444
|
-
const handleRotateImage = useCallback((degrees: -90 | 90) => {
|
|
458
|
+
const handleRotateImage = useCallback(async (degrees: -90 | 90) => {
|
|
445
459
|
if (!croppedImageData) return;
|
|
446
460
|
|
|
447
|
-
// UI 회전 상태 먼저 업데이트 (즉각 반응)
|
|
448
|
-
setRotationDegrees(prev => {
|
|
449
|
-
const newRotation = (prev + degrees + 360) % 360;
|
|
450
|
-
return newRotation;
|
|
451
|
-
});
|
|
452
|
-
|
|
453
461
|
console.log('[FullDocScanner] Starting image rotation...', {
|
|
454
462
|
path: croppedImageData.path,
|
|
455
463
|
hasBase64: !!croppedImageData.base64,
|
|
464
|
+
base64Length: croppedImageData.base64?.length,
|
|
456
465
|
});
|
|
457
466
|
|
|
458
|
-
|
|
459
|
-
|
|
467
|
+
try {
|
|
468
|
+
// 원본 파일을 ImageCropPicker로 열어서 회전 적용
|
|
469
|
+
// 사용자가 수동으로 회전 버튼을 클릭하고 완료를 눌러야 함
|
|
470
|
+
const rotatedImage = await ImageCropPicker.openCropper({
|
|
471
|
+
path: croppedImageData.path,
|
|
472
|
+
mediaType: 'photo',
|
|
473
|
+
cropping: true,
|
|
474
|
+
freeStyleCropEnabled: true,
|
|
475
|
+
includeBase64: true,
|
|
476
|
+
compressImageQuality: 0.9,
|
|
477
|
+
cropperToolbarTitle: degrees === 90 ? '우측 90° 회전 → 회전 버튼 클릭 → 완료' : '좌측 90° 회전 → 회전 버튼 클릭 → 완료',
|
|
478
|
+
cropperChooseText: '완료',
|
|
479
|
+
cropperCancelText: '취소',
|
|
480
|
+
cropperRotateButtonsHidden: false,
|
|
481
|
+
enableRotationGesture: true,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
console.log('[FullDocScanner] Rotated image saved:', {
|
|
485
|
+
path: rotatedImage.path,
|
|
486
|
+
hasBase64: !!rotatedImage.data,
|
|
487
|
+
base64Length: rotatedImage.data?.length,
|
|
488
|
+
});
|
|
460
489
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
console.log('[FullDocScanner] Image rotated successfully:', rotatedImageUri);
|
|
490
|
+
// 회전된 이미지로 교체
|
|
491
|
+
setCroppedImageData({
|
|
492
|
+
path: rotatedImage.path,
|
|
493
|
+
base64: rotatedImage.data ?? undefined,
|
|
494
|
+
});
|
|
467
495
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const base64Data = base64String.split(',')[1];
|
|
479
|
-
|
|
480
|
-
console.log('[FullDocScanner] Converted rotated image to base64, length:', base64Data?.length);
|
|
481
|
-
|
|
482
|
-
// 회전된 이미지로 교체 (base64 포함)
|
|
483
|
-
setCroppedImageData({
|
|
484
|
-
path: rotatedImageUri,
|
|
485
|
-
base64: base64Data,
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
// rotation degrees는 0으로 리셋
|
|
489
|
-
setRotationDegrees(0);
|
|
490
|
-
};
|
|
491
|
-
reader.readAsDataURL(blob);
|
|
492
|
-
} catch (convertError) {
|
|
493
|
-
console.error('[FullDocScanner] Failed to convert to base64:', convertError);
|
|
494
|
-
// base64 변환 실패 시 URI만 저장
|
|
495
|
-
setCroppedImageData({
|
|
496
|
-
path: rotatedImageUri,
|
|
497
|
-
base64: undefined,
|
|
498
|
-
});
|
|
499
|
-
setRotationDegrees(0);
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
(error: Error) => {
|
|
503
|
-
console.error('[FullDocScanner] Image rotation error:', error);
|
|
504
|
-
// 에러 발생 시 UI rotation 원복
|
|
505
|
-
setRotationDegrees(prev => {
|
|
506
|
-
const revertRotation = (prev - degrees + 360) % 360;
|
|
507
|
-
return revertRotation;
|
|
508
|
-
});
|
|
496
|
+
// rotation degrees는 0으로 리셋
|
|
497
|
+
setRotationDegrees(0);
|
|
498
|
+
} catch (error) {
|
|
499
|
+
console.error('[FullDocScanner] Image rotation error:', error);
|
|
500
|
+
|
|
501
|
+
// 사용자가 취소했으면 아무것도 안함
|
|
502
|
+
const errorMessage = error && typeof error === 'object' && 'message' in error ? (error as Error).message : '';
|
|
503
|
+
if (!errorMessage.includes('cancel') && !errorMessage.includes('User cancelled')) {
|
|
504
|
+
// 에러 메시지 표시
|
|
505
|
+
Alert.alert('회전 실패', '이미지 회전 중 오류가 발생했습니다.');
|
|
509
506
|
}
|
|
510
|
-
|
|
507
|
+
}
|
|
511
508
|
}, [croppedImageData]);
|
|
512
509
|
|
|
513
510
|
const handleConfirm = useCallback(() => {
|