react-native-rectangle-doc-scanner 3.98.0 → 3.99.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 +60 -41
- package/package.json +4 -2
- package/src/FullDocScanner.tsx +66 -48
package/dist/FullDocScanner.js
CHANGED
|
@@ -42,6 +42,7 @@ 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
44
|
const react_native_image_rotate_1 = __importDefault(require("react-native-image-rotate"));
|
|
45
|
+
const react_native_fs_1 = __importDefault(require("react-native-fs"));
|
|
45
46
|
const DocScanner_1 = require("./DocScanner");
|
|
46
47
|
const stripFileUri = (value) => value.replace(/^file:\/\//, '');
|
|
47
48
|
const CROPPER_TIMEOUT_MS = 8000;
|
|
@@ -220,9 +221,22 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
220
221
|
}
|
|
221
222
|
if (normalizedDoc.croppedPath) {
|
|
222
223
|
console.log('[FullDocScanner] Grid detected: using pre-cropped image', normalizedDoc.croppedPath);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
// RNFS를 사용해서 base64 생성
|
|
225
|
+
try {
|
|
226
|
+
const base64Data = await react_native_fs_1.default.readFile(normalizedDoc.croppedPath, 'base64');
|
|
227
|
+
console.log('[FullDocScanner] Generated base64 for pre-cropped image, length:', base64Data.length);
|
|
228
|
+
setCroppedImageData({
|
|
229
|
+
path: normalizedDoc.croppedPath,
|
|
230
|
+
base64: base64Data,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
console.error('[FullDocScanner] Failed to generate base64:', error);
|
|
235
|
+
// base64 생성 실패 시 경로만 저장
|
|
236
|
+
setCroppedImageData({
|
|
237
|
+
path: normalizedDoc.croppedPath,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
226
240
|
return;
|
|
227
241
|
}
|
|
228
242
|
console.log('[FullDocScanner] Fallback to manual crop (no croppedPath available)');
|
|
@@ -330,7 +344,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
330
344
|
const handleFlashToggle = (0, react_1.useCallback)(() => {
|
|
331
345
|
setFlashEnabled(prev => !prev);
|
|
332
346
|
}, []);
|
|
333
|
-
const handleRotateImage = (0, react_1.useCallback)((degrees) => {
|
|
347
|
+
const handleRotateImage = (0, react_1.useCallback)(async (degrees) => {
|
|
334
348
|
if (!croppedImageData)
|
|
335
349
|
return;
|
|
336
350
|
// UI 회전 상태 먼저 업데이트 (즉각 반응)
|
|
@@ -342,49 +356,54 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
342
356
|
path: croppedImageData.path,
|
|
343
357
|
hasBase64: !!croppedImageData.base64,
|
|
344
358
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const reader = new FileReader();
|
|
356
|
-
reader.onloadend = () => {
|
|
357
|
-
const base64String = reader.result;
|
|
358
|
-
// "data:image/jpeg;base64," 부분 제거
|
|
359
|
-
const base64Data = base64String.split(',')[1];
|
|
360
|
-
console.log('[FullDocScanner] Converted rotated image to base64, length:', base64Data?.length);
|
|
361
|
-
// 회전된 이미지로 교체 (base64 포함)
|
|
362
|
-
setCroppedImageData({
|
|
359
|
+
try {
|
|
360
|
+
// file:// prefix 제거
|
|
361
|
+
const cleanPath = croppedImageData.path.replace(/^file:\/\//, '');
|
|
362
|
+
// ImageRotate를 사용해서 이미지 회전
|
|
363
|
+
react_native_image_rotate_1.default.rotateImage(cleanPath, degrees, async (rotatedImageUri) => {
|
|
364
|
+
console.log('[FullDocScanner] Image rotated, URI:', rotatedImageUri);
|
|
365
|
+
try {
|
|
366
|
+
// 회전된 이미지를 실제 파일로 저장하기 위해 ImageCropPicker 사용
|
|
367
|
+
// cropping: true이지만 전체 이미지를 선택하면 됨
|
|
368
|
+
const savedImage = await react_native_image_crop_picker_1.default.openCropper({
|
|
363
369
|
path: rotatedImageUri,
|
|
364
|
-
|
|
370
|
+
mediaType: 'photo',
|
|
371
|
+
cropping: true,
|
|
372
|
+
freeStyleCropEnabled: true,
|
|
373
|
+
includeBase64: true,
|
|
374
|
+
compressImageQuality: 0.9,
|
|
375
|
+
cropperToolbarTitle: '회전 완료 - 확인을 눌러주세요',
|
|
376
|
+
cropperChooseText: '확인',
|
|
377
|
+
cropperCancelText: '취소',
|
|
378
|
+
cropperRotateButtonsHidden: true,
|
|
379
|
+
});
|
|
380
|
+
console.log('[FullDocScanner] Rotated image saved with base64');
|
|
381
|
+
// 회전된 이미지로 교체
|
|
382
|
+
setCroppedImageData({
|
|
383
|
+
path: savedImage.path,
|
|
384
|
+
base64: savedImage.data ?? undefined,
|
|
365
385
|
});
|
|
366
386
|
// rotation degrees는 0으로 리셋
|
|
367
387
|
setRotationDegrees(0);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
388
|
+
}
|
|
389
|
+
catch (cropError) {
|
|
390
|
+
console.error('[FullDocScanner] Failed to save rotated image:', cropError);
|
|
391
|
+
// 사용자가 취소한 경우 rotation 원복
|
|
392
|
+
setRotationDegrees(prev => (prev - degrees + 360) % 360);
|
|
393
|
+
}
|
|
394
|
+
}, (error) => {
|
|
395
|
+
console.error('[FullDocScanner] Image rotation error:', error);
|
|
396
|
+
// 에러 발생 시 UI rotation 원복
|
|
397
|
+
setRotationDegrees(prev => {
|
|
398
|
+
const revertRotation = (prev - degrees + 360) % 360;
|
|
399
|
+
return revertRotation;
|
|
377
400
|
});
|
|
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;
|
|
386
401
|
});
|
|
387
|
-
}
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
console.error('[FullDocScanner] Rotation setup error:', error);
|
|
405
|
+
setRotationDegrees(prev => (prev - degrees + 360) % 360);
|
|
406
|
+
}
|
|
388
407
|
}, [croppedImageData]);
|
|
389
408
|
const handleConfirm = (0, react_1.useCallback)(() => {
|
|
390
409
|
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.99.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,6 +33,7 @@
|
|
|
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
39
|
"react-native-image-rotate": "*",
|
|
@@ -41,11 +42,12 @@
|
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/react": "^18.2.41",
|
|
43
44
|
"@types/react-native": "0.73.0",
|
|
45
|
+
"@types/react-native-fs": "^2.8.3",
|
|
44
46
|
"react-native-image-crop-picker": "^0.41.2",
|
|
45
47
|
"react-native-image-picker": "^7.1.2",
|
|
46
48
|
"typescript": "^5.3.3"
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
49
|
-
"react-native-image-rotate": "
|
|
51
|
+
"react-native-image-rotate": "*"
|
|
50
52
|
}
|
|
51
53
|
}
|
package/src/FullDocScanner.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { launchImageLibrary } from 'react-native-image-picker';
|
|
13
13
|
import ImageCropPicker from 'react-native-image-crop-picker';
|
|
14
14
|
import ImageRotate from 'react-native-image-rotate';
|
|
15
|
+
import RNFS from 'react-native-fs';
|
|
15
16
|
import { DocScanner } from './DocScanner';
|
|
16
17
|
import type { CapturedDocument } from './types';
|
|
17
18
|
import type {
|
|
@@ -299,9 +300,23 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
299
300
|
|
|
300
301
|
if (normalizedDoc.croppedPath) {
|
|
301
302
|
console.log('[FullDocScanner] Grid detected: using pre-cropped image', normalizedDoc.croppedPath);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
|
|
304
|
+
// RNFS를 사용해서 base64 생성
|
|
305
|
+
try {
|
|
306
|
+
const base64Data = await RNFS.readFile(normalizedDoc.croppedPath, 'base64');
|
|
307
|
+
console.log('[FullDocScanner] Generated base64 for pre-cropped image, length:', base64Data.length);
|
|
308
|
+
|
|
309
|
+
setCroppedImageData({
|
|
310
|
+
path: normalizedDoc.croppedPath,
|
|
311
|
+
base64: base64Data,
|
|
312
|
+
});
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error('[FullDocScanner] Failed to generate base64:', error);
|
|
315
|
+
// base64 생성 실패 시 경로만 저장
|
|
316
|
+
setCroppedImageData({
|
|
317
|
+
path: normalizedDoc.croppedPath,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
305
320
|
return;
|
|
306
321
|
}
|
|
307
322
|
|
|
@@ -441,7 +456,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
441
456
|
setFlashEnabled(prev => !prev);
|
|
442
457
|
}, []);
|
|
443
458
|
|
|
444
|
-
const handleRotateImage = useCallback((degrees: -90 | 90) => {
|
|
459
|
+
const handleRotateImage = useCallback(async (degrees: -90 | 90) => {
|
|
445
460
|
if (!croppedImageData) return;
|
|
446
461
|
|
|
447
462
|
// UI 회전 상태 먼저 업데이트 (즉각 반응)
|
|
@@ -455,59 +470,62 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
455
470
|
hasBase64: !!croppedImageData.base64,
|
|
456
471
|
});
|
|
457
472
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
473
|
+
try {
|
|
474
|
+
// file:// prefix 제거
|
|
475
|
+
const cleanPath = croppedImageData.path.replace(/^file:\/\//, '');
|
|
476
|
+
|
|
477
|
+
// ImageRotate를 사용해서 이미지 회전
|
|
478
|
+
ImageRotate.rotateImage(
|
|
479
|
+
cleanPath,
|
|
480
|
+
degrees,
|
|
481
|
+
async (rotatedImageUri: string) => {
|
|
482
|
+
console.log('[FullDocScanner] Image rotated, URI:', rotatedImageUri);
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
// 회전된 이미지를 실제 파일로 저장하기 위해 ImageCropPicker 사용
|
|
486
|
+
// cropping: true이지만 전체 이미지를 선택하면 됨
|
|
487
|
+
const savedImage = await ImageCropPicker.openCropper({
|
|
488
|
+
path: rotatedImageUri,
|
|
489
|
+
mediaType: 'photo',
|
|
490
|
+
cropping: true,
|
|
491
|
+
freeStyleCropEnabled: true,
|
|
492
|
+
includeBase64: true,
|
|
493
|
+
compressImageQuality: 0.9,
|
|
494
|
+
cropperToolbarTitle: '회전 완료 - 확인을 눌러주세요',
|
|
495
|
+
cropperChooseText: '확인',
|
|
496
|
+
cropperCancelText: '취소',
|
|
497
|
+
cropperRotateButtonsHidden: true,
|
|
498
|
+
});
|
|
479
499
|
|
|
480
|
-
console.log('[FullDocScanner]
|
|
500
|
+
console.log('[FullDocScanner] Rotated image saved with base64');
|
|
481
501
|
|
|
482
|
-
// 회전된 이미지로 교체
|
|
502
|
+
// 회전된 이미지로 교체
|
|
483
503
|
setCroppedImageData({
|
|
484
|
-
path:
|
|
485
|
-
base64:
|
|
504
|
+
path: savedImage.path,
|
|
505
|
+
base64: savedImage.data ?? undefined,
|
|
486
506
|
});
|
|
487
507
|
|
|
488
508
|
// rotation degrees는 0으로 리셋
|
|
489
509
|
setRotationDegrees(0);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
510
|
+
} catch (cropError) {
|
|
511
|
+
console.error('[FullDocScanner] Failed to save rotated image:', cropError);
|
|
512
|
+
// 사용자가 취소한 경우 rotation 원복
|
|
513
|
+
setRotationDegrees(prev => (prev - degrees + 360) % 360);
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
(error: Error) => {
|
|
517
|
+
console.error('[FullDocScanner] Image rotation error:', error);
|
|
518
|
+
// 에러 발생 시 UI rotation 원복
|
|
519
|
+
setRotationDegrees(prev => {
|
|
520
|
+
const revertRotation = (prev - degrees + 360) % 360;
|
|
521
|
+
return revertRotation;
|
|
498
522
|
});
|
|
499
|
-
setRotationDegrees(0);
|
|
500
523
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const revertRotation = (prev - degrees + 360) % 360;
|
|
507
|
-
return revertRotation;
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
);
|
|
524
|
+
);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
console.error('[FullDocScanner] Rotation setup error:', error);
|
|
527
|
+
setRotationDegrees(prev => (prev - degrees + 360) % 360);
|
|
528
|
+
}
|
|
511
529
|
}, [croppedImageData]);
|
|
512
530
|
|
|
513
531
|
const handleConfirm = useCallback(() => {
|