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.
@@ -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 react_native_image_rotate_1 = __importDefault(require("react-native-image-rotate"));
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
- setCroppedImageData({
224
- path: normalizedDoc.croppedPath,
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
- // file:// prefix 제거
346
- const cleanPath = croppedImageData.path.replace(/^file:\/\//, '');
347
- // ImageRotate를 사용해서 실제로 이미지 회전 (callback 기반)
348
- react_native_image_rotate_1.default.rotateImage(cleanPath, degrees, async (rotatedImageUri) => {
349
- console.log('[FullDocScanner] Image rotated successfully:', rotatedImageUri);
350
- try {
351
- // rct-image-store:// URI를 base64로 변환
352
- const response = await fetch(rotatedImageUri);
353
- const blob = await response.blob();
354
- // Blob to base64
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({
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.98.0",
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
  }
@@ -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 ImageRotate from 'react-native-image-rotate';
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
- setCroppedImageData({
303
- path: normalizedDoc.croppedPath,
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
- // file:// prefix 제거
459
- const cleanPath = croppedImageData.path.replace(/^file:\/\//, '');
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
- // ImageRotate를 사용해서 실제로 이미지 회전 (callback 기반)
462
- ImageRotate.rotateImage(
463
- cleanPath,
464
- degrees,
465
- async (rotatedImageUri: string) => {
466
- console.log('[FullDocScanner] Image rotated successfully:', rotatedImageUri);
490
+ // 회전된 이미지로 교체
491
+ setCroppedImageData({
492
+ path: rotatedImage.path,
493
+ base64: rotatedImage.data ?? undefined,
494
+ });
467
495
 
468
- try {
469
- // rct-image-store:// URI를 base64로 변환
470
- const response = await fetch(rotatedImageUri);
471
- const blob = await response.blob();
472
-
473
- // Blob to base64
474
- const reader = new FileReader();
475
- reader.onloadend = () => {
476
- const base64String = reader.result as string;
477
- // "data:image/jpeg;base64," 부분 제거
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(() => {
@@ -1,10 +0,0 @@
1
- declare module 'react-native-image-rotate' {
2
- export default class ImageRotate {
3
- static rotateImage(
4
- imagePath: string,
5
- degrees: number,
6
- successCallback: (rotatedImagePath: string) => void,
7
- errorCallback: (error: Error) => void,
8
- ): void;
9
- }
10
- }