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.
@@ -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 회전 상태 먼저 업데이트 (즉각 반응)
@@ -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
- // ImageRotate를 사용해서 실제로 이미지 회전 (callback 기반)
343
- react_native_image_rotate_1.default.rotateImage(croppedImageData.path, degrees, async (rotatedImageUri) => {
344
- console.log('[FullDocScanner] Image rotated successfully:', rotatedImageUri);
345
- try {
346
- // rct-image-store:// URI를 base64로 변환
347
- const response = await fetch(rotatedImageUri);
348
- const blob = await response.blob();
349
- // Blob to base64
350
- const reader = new FileReader();
351
- reader.onloadend = () => {
352
- const base64String = reader.result;
353
- // "data:image/jpeg;base64," 부분 제거
354
- const base64Data = base64String.split(',')[1];
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
- 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,
360
385
  });
361
386
  // rotation degrees는 0으로 리셋
362
387
  setRotationDegrees(0);
363
- };
364
- reader.readAsDataURL(blob);
365
- }
366
- catch (convertError) {
367
- console.error('[FullDocScanner] Failed to convert to base64:', convertError);
368
- // base64 변환 실패 시 URI만 저장
369
- setCroppedImageData({
370
- path: rotatedImageUri,
371
- 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;
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.97.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 회전 상태 먼저 업데이트 (즉각 반응)
@@ -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
- // ImageRotate를 사용해서 실제로 이미지 회전 (callback 기반)
456
- ImageRotate.rotateImage(
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
- // Blob to base64
468
- const reader = new FileReader();
469
- reader.onloadend = () => {
470
- const base64String = reader.result as string;
471
- // "data:image/jpeg;base64," 부분 제거
472
- 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
+ });
473
499
 
474
- console.log('[FullDocScanner] Converted to base64, length:', base64Data?.length);
500
+ console.log('[FullDocScanner] Rotated image saved with base64');
475
501
 
476
- // 회전된 이미지로 교체 (base64 포함)
502
+ // 회전된 이미지로 교체
477
503
  setCroppedImageData({
478
- path: rotatedImageUri,
479
- base64: base64Data,
504
+ path: savedImage.path,
505
+ base64: savedImage.data ?? undefined,
480
506
  });
481
507
 
482
508
  // rotation degrees는 0으로 리셋
483
509
  setRotationDegrees(0);
484
- };
485
- reader.readAsDataURL(blob);
486
- } catch (convertError) {
487
- console.error('[FullDocScanner] Failed to convert to base64:', convertError);
488
- // base64 변환 실패 시 URI만 저장
489
- setCroppedImageData({
490
- path: rotatedImageUri,
491
- 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;
492
522
  });
493
- setRotationDegrees(0);
494
523
  }
495
- },
496
- (error: Error) => {
497
- console.error('[FullDocScanner] Image rotation error:', error);
498
- // 에러 발생 UI rotation 원복
499
- setRotationDegrees(prev => {
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(() => {