react-native-rectangle-doc-scanner 3.97.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 +64 -40
- package/package.json +4 -2
- package/src/FullDocScanner.tsx +70 -46
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 회전 상태 먼저 업데이트 (즉각 반응)
|
|
@@ -338,48 +352,58 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
338
352
|
const newRotation = (prev + degrees + 360) % 360;
|
|
339
353
|
return newRotation;
|
|
340
354
|
});
|
|
341
|
-
console.log('[FullDocScanner] Starting image rotation...'
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
//
|
|
354
|
-
const
|
|
355
|
-
console.log('[FullDocScanner] Converted to base64, length:', base64Data?.length);
|
|
356
|
-
// 회전된 이미지로 교체 (base64 포함)
|
|
357
|
-
setCroppedImageData({
|
|
355
|
+
console.log('[FullDocScanner] Starting image rotation...', {
|
|
356
|
+
path: croppedImageData.path,
|
|
357
|
+
hasBase64: !!croppedImageData.base64,
|
|
358
|
+
});
|
|
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({
|
|
358
369
|
path: rotatedImageUri,
|
|
359
|
-
|
|
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,
|
|
360
385
|
});
|
|
361
386
|
// rotation degrees는 0으로 리셋
|
|
362
387
|
setRotationDegrees(0);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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;
|
|
372
400
|
});
|
|
373
|
-
setRotationDegrees(0);
|
|
374
|
-
}
|
|
375
|
-
}, (error) => {
|
|
376
|
-
console.error('[FullDocScanner] Image rotation error:', error);
|
|
377
|
-
// 에러 발생 시 UI rotation 원복
|
|
378
|
-
setRotationDegrees(prev => {
|
|
379
|
-
const revertRotation = (prev - degrees + 360) % 360;
|
|
380
|
-
return revertRotation;
|
|
381
401
|
});
|
|
382
|
-
}
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
console.error('[FullDocScanner] Rotation setup error:', error);
|
|
405
|
+
setRotationDegrees(prev => (prev - degrees + 360) % 360);
|
|
406
|
+
}
|
|
383
407
|
}, [croppedImageData]);
|
|
384
408
|
const handleConfirm = (0, react_1.useCallback)(() => {
|
|
385
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 회전 상태 먼저 업데이트 (즉각 반응)
|
|
@@ -450,58 +465,67 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
450
465
|
return newRotation;
|
|
451
466
|
});
|
|
452
467
|
|
|
453
|
-
console.log('[FullDocScanner] Starting image rotation...'
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
croppedImageData.path,
|
|
458
|
-
degrees,
|
|
459
|
-
async (rotatedImageUri: string) => {
|
|
460
|
-
console.log('[FullDocScanner] Image rotated successfully:', rotatedImageUri);
|
|
461
|
-
|
|
462
|
-
try {
|
|
463
|
-
// rct-image-store:// URI를 base64로 변환
|
|
464
|
-
const response = await fetch(rotatedImageUri);
|
|
465
|
-
const blob = await response.blob();
|
|
468
|
+
console.log('[FullDocScanner] Starting image rotation...', {
|
|
469
|
+
path: croppedImageData.path,
|
|
470
|
+
hasBase64: !!croppedImageData.base64,
|
|
471
|
+
});
|
|
466
472
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
+
});
|
|
473
499
|
|
|
474
|
-
console.log('[FullDocScanner]
|
|
500
|
+
console.log('[FullDocScanner] Rotated image saved with base64');
|
|
475
501
|
|
|
476
|
-
// 회전된 이미지로 교체
|
|
502
|
+
// 회전된 이미지로 교체
|
|
477
503
|
setCroppedImageData({
|
|
478
|
-
path:
|
|
479
|
-
base64:
|
|
504
|
+
path: savedImage.path,
|
|
505
|
+
base64: savedImage.data ?? undefined,
|
|
480
506
|
});
|
|
481
507
|
|
|
482
508
|
// rotation degrees는 0으로 리셋
|
|
483
509
|
setRotationDegrees(0);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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;
|
|
492
522
|
});
|
|
493
|
-
setRotationDegrees(0);
|
|
494
523
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const revertRotation = (prev - degrees + 360) % 360;
|
|
501
|
-
return revertRotation;
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
);
|
|
524
|
+
);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
console.error('[FullDocScanner] Rotation setup error:', error);
|
|
527
|
+
setRotationDegrees(prev => (prev - degrees + 360) % 360);
|
|
528
|
+
}
|
|
505
529
|
}, [croppedImageData]);
|
|
506
530
|
|
|
507
531
|
const handleConfirm = useCallback(() => {
|