npm package: @platox/expo-document-scanner
Description: Expo document scanner module with camera view, document detection and image processing
Author: Bing Han
Latest version: 0.1.2

# expo-document-scanner

Expo native module for document scanning with real-time detection, auto-capture, manual crop, and image enhancement. Supports iOS and Android.

## Features

- Real-time document edge detection with camera preview
- Auto-capture when document is stable and sharp (multi-dimension quality scoring)
- Manual mode with draggable corner handles for precise cropping
- Fullscreen crop view with safe area support
- Standalone `cropImage` API — crop any image without the camera
- Perspective correction and image enhancement (white balance, contrast, sharpening)
- iOS system document scanner (`VNDocumentCameraViewController`) support
- Kalman filter corner smoothing for stable overlay

## Installation

```bash
npm install expo-document-scanner
```

### iOS

```bash
npx pod-install
```

### Android

No additional configuration needed.

## Usage

### 1. Camera Scanner View

```tsx
import { DocumentScannerView } from 'expo-document-scanner';

function Scanner() {
  return (
    <DocumentScannerView
      style={{ flex: 1 }}
      autoCapture={true}
      enableImageEnhancement={true}
      overlayStyle="quadrilateral"
      captureQualityThreshold={75}
      onCapture={(e) => {
        const { uri, width, height } = e.nativeEvent;
        console.log('Captured:', uri);
      }}
      onDocumentDetected={(e) => {
        const { detected, qualityScore } = e.nativeEvent;
      }}
      onError={(e) => {
        console.error(e.nativeEvent.code, e.nativeEvent.message);
      }}
    />
  );
}
```

### 2. Manual Mode

```tsx
<DocumentScannerView
  style={{ flex: 1 }}
  manualMode={true}
  onCapture={(e) => {
    // Photo is captured after user adjusts corners and confirms
    // ManualCropView covers the entire screen (fullscreen)
    console.log('Cropped:', e.nativeEvent.uri);
  }}
/>
```

### 3. Standalone Crop API

Crop any existing image without the camera. Opens a fullscreen manual crop view with auto-detected document edges.

```typescript
import { ExpoDocumentScanner } from 'expo-document-scanner';

const result = await ExpoDocumentScanner.cropImage('file:///path/to/photo.jpg', {
  enableImageEnhancement: true,
  // initialCorners: [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 }],
});

if (!result.cancelled) {
  console.log(result.uri);        // file:///tmp/cropped_xxx.jpg
  console.log(result.base64Uri);  // data:image/jpeg;base64,...
  console.log(result.width, result.height);
  console.log(result.corners);    // actual corners used
}
```

Supported URI formats: `file://`, `data:` (base64), `content://` (Android).

### 4. iOS System Document Scanner

```typescript
import { ExpoDocumentScanner } from 'expo-document-scanner';

if (ExpoDocumentScanner.isDocumentScannerAvailable()) {
  const result = await ExpoDocumentScanner.openDocumentScanner();
  if (!result.cancelled) {
    result.pages.forEach((page) => {
      console.log(page.uri, page.width, page.height);
    });
  }
}
```

### 5. Camera Permissions

Use the cross-platform helpers exported from the package. They handle platform differences internally — `PermissionsAndroid` on Android, `AVCaptureDevice` on iOS.

```typescript
import { requestCameraPermission, getCameraPermissionStatus } from 'expo-document-scanner';

const status = getCameraPermissionStatus();
// 'authorized' | 'denied' | 'restricted' | 'notDetermined' | 'unknown'

if (status !== 'authorized') {
  const granted = await requestCameraPermission();
}
```

#### Permission flow

```
Enter camera screen
       |
       v
getCameraPermissionStatus()        (sync, native on both platforms)
       |
       |-- "authorized"       -->  Render camera
       |
       |-- "notDetermined"    -->  requestCameraPermission()
       |                              |
       |                     +--------+--------+
       |                  Android             iOS
       |              PermissionsAndroid   AVCaptureDevice
       |              .request()           .requestAccess()
       |                     |                |
       |                     +-- OS dialog ---+
       |                            |
       |                     +------+------+
       |                   Allow        Deny
       |                     |            |
       |                Render camera  Show fallback UI
       |
       |-- "denied"          -->  requestCameraPermission()
       |                          (Android: OS dialog can appear again)
       |
       +-- "restricted"      -->  Show fallback UI + "Open Settings" button
                                  (permanently denied, user must enable manually)
```

#### Status values

| Status | iOS | Android |
|--------|-----|---------|
| `notDetermined` | Never asked | Never asked |
| `authorized` | User allowed | User allowed |
| `denied` | User denied | User denied (can ask again) |
| `restricted` | Restricted by system (e.g. parental controls) | Denied with "Don't ask again" |
| `unknown` | Unable to determine | Unable to determine |

## API Reference

### `DocumentScannerView` Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `autoCapture` | `boolean` | `true` | Auto-capture when document is stable and sharp |
| `sharpnessThreshold` | `number` | `100` | Minimum sharpness for auto-capture (50-200) |
| `stabilityFramesRequired` | `number` | `5` | Consecutive stable frames before auto-capture |
| `minDocumentArea` | `number` | `0.1` | Minimum document area ratio (0-1) |
| `enableFlash` | `boolean` | `false` | Enable flash |
| `showDetectionOverlay` | `boolean` | `true` | Show detection overlay |
| `enableImageEnhancement` | `boolean` | `true` | Apply enhancement after capture |
| `overlayStyle` | `'quadrilateral' \| 'rectangle'` | `'quadrilateral'` | Overlay shape style |
| `captureQualityThreshold` | `number` | `75` | Quality score threshold (0-100) for auto-capture |
| `manualMode` | `boolean` | `false` | Enable manual capture + corner adjustment |

### `DocumentScannerView` Events

| Event | Payload | Description |
|-------|---------|-------------|
| `onDocumentDetected` | `DocumentDetectedEventPayload` | Document detected/lost in frame |
| `onCapture` | `CaptureEventPayload` | Photo captured (auto or manual) |
| `onError` | `ErrorEventPayload` | Error occurred |
| `onStatusChange` | `StatusChangeEventPayload` | Scanner status changed |

### Module Functions

| Function | Returns | Description |
|----------|---------|-------------|
| `isCameraAvailable()` | `boolean` | Check camera availability |
| `requestCameraPermission()` | `Promise<boolean>` | Request camera permission |
| `getCameraPermissionStatus()` | `CameraPermissionStatus` | Get current permission status |
| `isDocumentScannerAvailable()` | `boolean` | Check system scanner availability (iOS only) |
| `openDocumentScanner()` | `Promise<DocumentScannerResult>` | Open iOS system document scanner |
| `cropImage(uri, options?)` | `Promise<CropImageResult>` | Open fullscreen crop view for an existing image |

### `CropImageOptions`

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enableImageEnhancement` | `boolean` | `true` | Apply enhancement after cropping |
| `initialCorners` | `Point[]` | auto-detect | Initial crop corners (normalized 0-1). Auto-detects document edges if omitted. |

### `CropImageResult`

| Field | Type | Description |
|-------|------|-------------|
| `uri` | `string` | Cropped image `file://` URI |
| `base64Uri` | `string` | Cropped image as `data:image/jpeg;base64,...` |
| `width` | `number` | Image width in pixels |
| `height` | `number` | Image height in pixels |
| `corners` | `Point[]` | Actual crop corners used |
| `cancelled` | `boolean` | `true` if user cancelled |

## Platform Support

| Feature | iOS | Android | Web |
|---------|-----|---------|-----|
| Camera scanner view | Yes | Yes | No |
| Auto-capture | Yes (ML-based detection) | Yes (edge detection) | No |
| Manual crop | Yes (fullscreen) | Yes (fullscreen) | No |
| `cropImage` API | Yes (with auto-detect) | Yes | No |
| System document scanner | Yes (VNDocumentCameraViewController) | No | No |
| Image enhancement | Yes (Core Image) | Yes (ColorMatrix) | No |

## Architecture

```
ios/
  ExpoDocumentScannerModule.swift   # Module: functions + cropImage API
  ExpoDocumentScannerView.swift     # Camera view with detection + auto-capture
  ManualCropView.swift              # Fullscreen corner adjustment UI
  DocumentImageProcessor.swift      # Shared: perspective correction, enhancement, detection

android/.../
  ExpoDocumentScannerModule.kt      # Module: functions + cropImage API
  ExpoDocumentScannerView.kt        # Camera view with detection + auto-capture
  ManualCropView.kt                 # Fullscreen corner adjustment UI
  DocumentImageProcessor.kt         # Shared: perspective correction, enhancement, loading

src/
  index.ts                          # Public exports
  ExpoDocumentScannerModule.ts      # Native module type declarations
  ExpoDocumentScannerModule.web.ts  # Web stubs
  ExpoDocumentScanner.types.ts      # TypeScript type definitions
  ExpoDocumentScannerView.tsx       # React component wrapper
```

## License

MIT