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.
@@ -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
- setCroppedImageData({
224
- path: normalizedDoc.croppedPath,
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
- // 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({
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
- base64: base64Data,
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
- 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,
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.98.0",
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": "^2.1.0"
51
+ "react-native-image-rotate": "*"
50
52
  }
51
53
  }
@@ -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
- setCroppedImageData({
303
- path: normalizedDoc.croppedPath,
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
- // file:// prefix 제거
459
- const cleanPath = croppedImageData.path.replace(/^file:\/\//, '');
460
-
461
- // ImageRotate를 사용해서 실제로 이미지 회전 (callback 기반)
462
- ImageRotate.rotateImage(
463
- cleanPath,
464
- degrees,
465
- async (rotatedImageUri: string) => {
466
- console.log('[FullDocScanner] Image rotated successfully:', rotatedImageUri);
467
-
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];
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] Converted rotated image to base64, length:', base64Data?.length);
500
+ console.log('[FullDocScanner] Rotated image saved with base64');
481
501
 
482
- // 회전된 이미지로 교체 (base64 포함)
502
+ // 회전된 이미지로 교체
483
503
  setCroppedImageData({
484
- path: rotatedImageUri,
485
- base64: base64Data,
504
+ path: savedImage.path,
505
+ base64: savedImage.data ?? undefined,
486
506
  });
487
507
 
488
508
  // rotation degrees는 0으로 리셋
489
509
  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,
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
- (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
- });
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(() => {