react-native-rectangle-doc-scanner 3.109.0 → 3.110.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.
@@ -45,7 +45,6 @@ const react_native_fs_1 = __importDefault(require("react-native-fs"));
45
45
  const DocScanner_1 = require("./DocScanner");
46
46
  let ImageManipulator = null;
47
47
  let ImageRotate = null;
48
- const ImageStoreManager = react_native_1.NativeModules.ImageStoreManager;
49
48
  try {
50
49
  ImageManipulator = require('expo-image-manipulator');
51
50
  }
@@ -65,24 +64,6 @@ const isImageRotateAvailable = !!ImageRotate?.rotateImage;
65
64
  const isImageRotationSupported = () => isExpoImageManipulatorAvailable() || isImageRotateAvailable;
66
65
  const stripFileUri = (value) => value.replace(/^file:\/\//, '');
67
66
  const ensureFileUri = (value) => (value.startsWith('file://') ? value : `file://${value}`);
68
- const getBase64FromImageStore = async (uri) => {
69
- const getBase64ForTag = ImageStoreManager?.getBase64ForTag?.bind(ImageStoreManager);
70
- if (!getBase64ForTag) {
71
- throw new Error('ImageStoreManager.getBase64ForTag unavailable');
72
- }
73
- return new Promise((resolve, reject) => {
74
- getBase64ForTag(uri, (base64) => resolve(base64), (error) => {
75
- const message = typeof error === 'string'
76
- ? error
77
- : error && typeof error === 'object' && 'message' in error
78
- ? String(error.message)
79
- : 'Failed to read from ImageStore';
80
- reject(new Error(message));
81
- });
82
- }).finally(() => {
83
- ImageStoreManager?.removeImageForTag?.(uri);
84
- });
85
- };
86
67
  const CROPPER_TIMEOUT_MS = 8000;
87
68
  const CROPPER_TIMEOUT_CODE = 'cropper_timeout';
88
69
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -418,8 +399,9 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
418
399
  if (!croppedImageData) {
419
400
  return;
420
401
  }
421
- // 회전이 없거나 모듈을 사용할 수 없으면 기존 데이터 그대로 전달
402
+ // 회전이 없으면 기존 데이터 그대로 전달
422
403
  if (rotationDegrees === 0) {
404
+ console.log('[FullDocScanner] No rotation, returning original image');
423
405
  onResult({
424
406
  path: croppedImageData.path,
425
407
  base64: croppedImageData.base64,
@@ -430,74 +412,103 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
430
412
  setProcessing(true);
431
413
  // 회전 각도 정규화 (0, 90, 180, 270)
432
414
  const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
415
+ console.log('[FullDocScanner] Rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
433
416
  if (rotationNormalized === 0) {
417
+ console.log('[FullDocScanner] Normalized to 0, returning original image');
434
418
  onResult({
435
419
  path: croppedImageData.path,
436
420
  base64: croppedImageData.base64,
437
421
  });
422
+ setProcessing(false);
438
423
  return;
439
424
  }
425
+ // expo-image-manipulator 시도
440
426
  if (isExpoImageManipulatorAvailable() && ImageManipulator?.manipulateAsync) {
441
427
  try {
442
- // expo-image-manipulator 이미지 회전
443
- const result = await ImageManipulator.manipulateAsync(croppedImageData.path, [{ rotate: rotationNormalized }], {
428
+ console.log('[FullDocScanner] Using expo-image-manipulator for rotation');
429
+ const inputUri = ensureFileUri(croppedImageData.path);
430
+ const result = await ImageManipulator.manipulateAsync(inputUri, [{ rotate: rotationNormalized }], {
444
431
  compress: 0.9,
445
432
  format: ImageManipulator.SaveFormat.JPEG,
446
433
  base64: true,
447
434
  });
448
- onResult({
435
+ console.log('[FullDocScanner] Rotation complete via expo-image-manipulator:', {
449
436
  path: result.uri,
437
+ hasBase64: !!result.base64,
438
+ });
439
+ onResult({
440
+ path: stripFileUri(result.uri),
450
441
  base64: result.base64,
451
442
  });
443
+ setProcessing(false);
452
444
  return;
453
445
  }
454
446
  catch (manipulatorError) {
455
447
  const code = manipulatorError && typeof manipulatorError === 'object' && 'code' in manipulatorError
456
448
  ? String(manipulatorError.code)
457
449
  : undefined;
450
+ console.error('[FullDocScanner] expo-image-manipulator error:', manipulatorError);
458
451
  if (code === 'ERR_UNAVAILABLE') {
459
452
  expoManipulatorUnavailable = true;
460
- console.warn('[FullDocScanner] expo-image-manipulator unavailable at runtime. Falling back to react-native-image-rotate if possible.');
453
+ console.warn('[FullDocScanner] expo-image-manipulator unavailable at runtime. Trying react-native-image-rotate.');
461
454
  }
462
455
  else {
463
456
  throw manipulatorError;
464
457
  }
465
458
  }
466
459
  }
460
+ // react-native-image-rotate 시도 (ImageStore 사용 안 함)
467
461
  if (isImageRotateAvailable && ImageRotate?.rotateImage) {
462
+ console.log('[FullDocScanner] Using react-native-image-rotate for rotation');
468
463
  const sourceUri = ensureFileUri(croppedImageData.path);
469
- const rotatedUri = await rotateImageWithFallback(sourceUri, rotationNormalized);
470
- let finalPath = croppedImageData.path;
471
- let base64Result = croppedImageData.base64;
472
464
  try {
473
- const base64FromStore = await getBase64FromImageStore(rotatedUri);
474
- const destinationPath = `${react_native_fs_1.default.CachesDirectoryPath}/full-doc-scanner-rotated-${Date.now()}-${Math.floor(Math.random() * 10000)}.jpg`;
475
- await react_native_fs_1.default.writeFile(destinationPath, base64FromStore, 'base64');
476
- finalPath = destinationPath;
477
- base64Result = base64FromStore;
465
+ const rotatedUri = await rotateImageWithFallback(sourceUri, rotationNormalized);
466
+ console.log('[FullDocScanner] Image rotated via react-native-image-rotate:', rotatedUri);
467
+ // rotatedUri를 파일로 저장하고 base64 생성
468
+ const destinationPath = `${react_native_fs_1.default.CachesDirectoryPath}/rotated_img_${Date.now()}.jpeg`;
469
+ // rotatedUri는 이미 file://로 시작하므로 stripFileUri로 정리
470
+ const cleanRotatedUri = stripFileUri(rotatedUri);
471
+ // 파일 복사
472
+ await react_native_fs_1.default.copyFile(cleanRotatedUri, destinationPath);
473
+ console.log('[FullDocScanner] Copied rotated file to:', destinationPath);
474
+ // Base64 생성
475
+ const base64Data = await react_native_fs_1.default.readFile(destinationPath, 'base64');
476
+ console.log('[FullDocScanner] Got result:', {
477
+ path: destinationPath,
478
+ hasBase64: true,
479
+ base64Length: base64Data.length,
480
+ });
481
+ onResult({
482
+ path: destinationPath,
483
+ base64: base64Data,
484
+ });
485
+ setProcessing(false);
486
+ return;
478
487
  }
479
- catch (readError) {
480
- console.warn('[FullDocScanner] Failed to persist rotated image from ImageStore:', readError);
488
+ catch (rotateError) {
489
+ console.error('[FullDocScanner] react-native-image-rotate error:', rotateError);
490
+ throw rotateError;
481
491
  }
482
- onResult({
483
- path: finalPath,
484
- base64: base64Result,
485
- });
486
- return;
487
492
  }
493
+ // 회전 불가능한 경우 경고 후 원본 반환
488
494
  console.warn('[FullDocScanner] Rotation requested but no supported rotation module is available. Returning original image.');
495
+ react_native_1.Alert.alert('알림', '이미지 회전 기능을 사용할 수 없습니다. 원본 이미지를 사용합니다.');
489
496
  onResult({
490
497
  path: croppedImageData.path,
491
498
  base64: croppedImageData.base64,
492
499
  });
500
+ setProcessing(false);
493
501
  }
494
502
  catch (error) {
495
503
  console.error('[FullDocScanner] Image rotation error on confirm:', error);
496
- const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : '';
497
- react_native_1.Alert.alert('회전 실패', '이미지 회전 중 오류가 발생했습니다: ' + errorMessage);
498
- }
499
- finally {
500
504
  setProcessing(false);
505
+ const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
506
+ react_native_1.Alert.alert('회전 실패', `이미지 회전 중 오류가 발생했습니다.\n원본 이미지를 사용합니다.\n\n오류: ${errorMessage}`);
507
+ // 에러 발생 시 원본 이미지 반환
508
+ onResult({
509
+ path: croppedImageData.path,
510
+ base64: croppedImageData.base64,
511
+ });
501
512
  }
502
513
  }, [croppedImageData, rotationDegrees, onResult, rotateImageWithFallback]);
503
514
  const handleRetake = (0, react_1.useCallback)(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.109.0",
3
+ "version": "3.110.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,6 @@ import {
8
8
  Text,
9
9
  TouchableOpacity,
10
10
  View,
11
- NativeModules,
12
11
  } from 'react-native';
13
12
  import { launchImageLibrary } from 'react-native-image-picker';
14
13
  import ImageCropPicker from 'react-native-image-crop-picker';
@@ -32,18 +31,8 @@ type ImageRotateModule = {
32
31
  ) => void;
33
32
  } | null;
34
33
 
35
- type ImageStoreModule = {
36
- getBase64ForTag?: (
37
- uri: string,
38
- success: (base64: string) => void,
39
- failure: (error: unknown) => void,
40
- ) => void;
41
- removeImageForTag?: (uri: string) => void;
42
- } | undefined;
43
-
44
34
  let ImageManipulator: ImageManipulatorModule | null = null;
45
35
  let ImageRotate: ImageRotateModule = null;
46
- const ImageStoreManager: ImageStoreModule = NativeModules.ImageStoreManager;
47
36
 
48
37
  try {
49
38
  ImageManipulator = require('expo-image-manipulator') as ImageManipulatorModule;
@@ -73,32 +62,6 @@ const isImageRotationSupported = () => isExpoImageManipulatorAvailable() || isIm
73
62
  const stripFileUri = (value: string) => value.replace(/^file:\/\//, '');
74
63
  const ensureFileUri = (value: string) => (value.startsWith('file://') ? value : `file://${value}`);
75
64
 
76
- const getBase64FromImageStore = async (uri: string): Promise<string> => {
77
- const getBase64ForTag = ImageStoreManager?.getBase64ForTag?.bind(ImageStoreManager);
78
-
79
- if (!getBase64ForTag) {
80
- throw new Error('ImageStoreManager.getBase64ForTag unavailable');
81
- }
82
-
83
- return new Promise<string>((resolve, reject) => {
84
- getBase64ForTag(
85
- uri,
86
- (base64: string) => resolve(base64),
87
- (error: unknown) => {
88
- const message =
89
- typeof error === 'string'
90
- ? error
91
- : error && typeof error === 'object' && 'message' in error
92
- ? String((error as any).message)
93
- : 'Failed to read from ImageStore';
94
- reject(new Error(message));
95
- },
96
- );
97
- }).finally(() => {
98
- ImageStoreManager?.removeImageForTag?.(uri);
99
- });
100
- };
101
-
102
65
  const CROPPER_TIMEOUT_MS = 8000;
103
66
  const CROPPER_TIMEOUT_CODE = 'cropper_timeout';
104
67
 
@@ -581,8 +544,9 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
581
544
  return;
582
545
  }
583
546
 
584
- // 회전이 없거나 모듈을 사용할 수 없으면 기존 데이터 그대로 전달
547
+ // 회전이 없으면 기존 데이터 그대로 전달
585
548
  if (rotationDegrees === 0) {
549
+ console.log('[FullDocScanner] No rotation, returning original image');
586
550
  onResult({
587
551
  path: croppedImageData.path,
588
552
  base64: croppedImageData.base64,
@@ -595,20 +559,26 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
595
559
 
596
560
  // 회전 각도 정규화 (0, 90, 180, 270)
597
561
  const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
562
+ console.log('[FullDocScanner] Rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
598
563
 
599
564
  if (rotationNormalized === 0) {
565
+ console.log('[FullDocScanner] Normalized to 0, returning original image');
600
566
  onResult({
601
567
  path: croppedImageData.path,
602
568
  base64: croppedImageData.base64,
603
569
  });
570
+ setProcessing(false);
604
571
  return;
605
572
  }
606
573
 
574
+ // expo-image-manipulator 시도
607
575
  if (isExpoImageManipulatorAvailable() && ImageManipulator?.manipulateAsync) {
608
576
  try {
609
- // expo-image-manipulator 이미지 회전
577
+ console.log('[FullDocScanner] Using expo-image-manipulator for rotation');
578
+ const inputUri = ensureFileUri(croppedImageData.path);
579
+
610
580
  const result = await ImageManipulator.manipulateAsync(
611
- croppedImageData.path,
581
+ inputUri,
612
582
  [{ rotate: rotationNormalized }],
613
583
  {
614
584
  compress: 0.9,
@@ -617,10 +587,16 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
617
587
  },
618
588
  );
619
589
 
620
- onResult({
590
+ console.log('[FullDocScanner] Rotation complete via expo-image-manipulator:', {
621
591
  path: result.uri,
592
+ hasBase64: !!result.base64,
593
+ });
594
+
595
+ onResult({
596
+ path: stripFileUri(result.uri),
622
597
  base64: result.base64,
623
598
  });
599
+ setProcessing(false);
624
600
  return;
625
601
  } catch (manipulatorError) {
626
602
  const code =
@@ -628,10 +604,12 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
628
604
  ? String((manipulatorError as any).code)
629
605
  : undefined;
630
606
 
607
+ console.error('[FullDocScanner] expo-image-manipulator error:', manipulatorError);
608
+
631
609
  if (code === 'ERR_UNAVAILABLE') {
632
610
  expoManipulatorUnavailable = true;
633
611
  console.warn(
634
- '[FullDocScanner] expo-image-manipulator unavailable at runtime. Falling back to react-native-image-rotate if possible.',
612
+ '[FullDocScanner] expo-image-manipulator unavailable at runtime. Trying react-native-image-rotate.',
635
613
  );
636
614
  } else {
637
615
  throw manipulatorError;
@@ -639,46 +617,68 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
639
617
  }
640
618
  }
641
619
 
620
+ // react-native-image-rotate 시도 (ImageStore 사용 안 함)
642
621
  if (isImageRotateAvailable && ImageRotate?.rotateImage) {
622
+ console.log('[FullDocScanner] Using react-native-image-rotate for rotation');
643
623
  const sourceUri = ensureFileUri(croppedImageData.path);
644
- const rotatedUri = await rotateImageWithFallback(sourceUri, rotationNormalized);
645
-
646
- let finalPath = croppedImageData.path;
647
- let base64Result: string | undefined = croppedImageData.base64;
648
624
 
649
625
  try {
650
- const base64FromStore = await getBase64FromImageStore(rotatedUri);
651
- const destinationPath = `${RNFS.CachesDirectoryPath}/full-doc-scanner-rotated-${Date.now()}-${Math.floor(Math.random() * 10000)}.jpg`;
652
-
653
- await RNFS.writeFile(destinationPath, base64FromStore, 'base64');
654
- finalPath = destinationPath;
655
- base64Result = base64FromStore;
656
- } catch (readError) {
657
- console.warn('[FullDocScanner] Failed to persist rotated image from ImageStore:', readError);
658
- }
626
+ const rotatedUri = await rotateImageWithFallback(sourceUri, rotationNormalized);
627
+ console.log('[FullDocScanner] Image rotated via react-native-image-rotate:', rotatedUri);
659
628
 
660
- onResult({
661
- path: finalPath,
662
- base64: base64Result,
663
- });
664
- return;
629
+ // rotatedUri를 파일로 저장하고 base64 생성
630
+ const destinationPath = `${RNFS.CachesDirectoryPath}/rotated_img_${Date.now()}.jpeg`;
631
+
632
+ // rotatedUri는 이미 file://로 시작하므로 stripFileUri로 정리
633
+ const cleanRotatedUri = stripFileUri(rotatedUri);
634
+
635
+ // 파일 복사
636
+ await RNFS.copyFile(cleanRotatedUri, destinationPath);
637
+ console.log('[FullDocScanner] Copied rotated file to:', destinationPath);
638
+
639
+ // Base64 생성
640
+ const base64Data = await RNFS.readFile(destinationPath, 'base64');
641
+ console.log('[FullDocScanner] Got result:', {
642
+ path: destinationPath,
643
+ hasBase64: true,
644
+ base64Length: base64Data.length,
645
+ });
646
+
647
+ onResult({
648
+ path: destinationPath,
649
+ base64: base64Data,
650
+ });
651
+ setProcessing(false);
652
+ return;
653
+ } catch (rotateError) {
654
+ console.error('[FullDocScanner] react-native-image-rotate error:', rotateError);
655
+ throw rotateError;
656
+ }
665
657
  }
666
658
 
659
+ // 회전 불가능한 경우 경고 후 원본 반환
667
660
  console.warn(
668
661
  '[FullDocScanner] Rotation requested but no supported rotation module is available. Returning original image.',
669
662
  );
663
+ Alert.alert('알림', '이미지 회전 기능을 사용할 수 없습니다. 원본 이미지를 사용합니다.');
670
664
  onResult({
671
665
  path: croppedImageData.path,
672
666
  base64: croppedImageData.base64,
673
667
  });
668
+ setProcessing(false);
674
669
  } catch (error) {
675
670
  console.error('[FullDocScanner] Image rotation error on confirm:', error);
671
+ setProcessing(false);
676
672
 
677
673
  const errorMessage =
678
- error && typeof error === 'object' && 'message' in error ? (error as Error).message : '';
679
- Alert.alert('회전 실패', '이미지 회전 중 오류가 발생했습니다: ' + errorMessage);
680
- } finally {
681
- setProcessing(false);
674
+ error && typeof error === 'object' && 'message' in error ? (error as Error).message : String(error);
675
+ Alert.alert('회전 실패', `이미지 회전 중 오류가 발생했습니다.\n원본 이미지를 사용합니다.\n\n오류: ${errorMessage}`);
676
+
677
+ // 에러 발생 시 원본 이미지 반환
678
+ onResult({
679
+ path: croppedImageData.path,
680
+ base64: croppedImageData.base64,
681
+ });
682
682
  }
683
683
  }, [croppedImageData, rotationDegrees, onResult, rotateImageWithFallback]);
684
684