Metadata-Version: 2.4
Name: n1r-parser-gate
Version: 0.1.1
Summary: Unified document intake, type-detection, conversion, and validation library
Requires-Python: >=3.10
Requires-Dist: charset-normalizer>=3.0.0
Requires-Dist: img2pdf>=0.5.0
Requires-Dist: loguru>=0.7.0
Requires-Dist: magika>=0.5.0
Requires-Dist: pillow-heif>=0.18.0
Requires-Dist: pillow>=10.0.0
Requires-Dist: pypdf>=4.0.0
Description-Content-Type: text/markdown

# ParserGate

**Unified document intake, type-detection, conversion, and validation for Python.**

![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)

## Overview

ParserGate standardizes diverse file inputs into a single, validated PDF artifact for downstream parsing pipelines. It handles file type detection, format conversion, and output validation in one unified API.

## Features

- **Multiple Format Support**: PDF, Images (JPEG, PNG, TIFF, BMP, WebP), Office documents (DOCX, PPTX), and Text files
- **Intelligent Detection**: File type detection using [Magika](https://github.com/google/magika) with confidence thresholds
- **Image Processing**: EXIF orientation correction, multi-page TIFF extraction, batch image merging
- **PDF Validation**: Structure validation, encryption detection, page count verification
- **Cross-Platform**: LibreOffice integration for macOS, Linux, and Windows

## Installation

```bash
pip install n1r-parser-gate
```

### External Dependency

ParserGate requires **LibreOffice** for converting Office documents (DOCX, PPTX) and text files:

- **macOS**: `brew install --cask libreoffice`
- **Ubuntu/Debian**: `sudo apt install libreoffice`
- **Windows**: Download from [libreoffice.org](https://www.libreoffice.org/download/download/)

## Quick Start

### Basic Conversion

```python
from pathlib import Path
from n1r.parser_gate import process

# Convert a Word document to PDF
result = process(
    inputs=[Path("document.docx")],
    output_path=Path("output.pdf"),
)

print(f"Created: {result.output_path}")
print(f"Detected as: {result.detected_type} ({result.confidence:.0%})")
```

### Batch Images

```python
from pathlib import Path
from n1r.parser_gate import process

# Combine multiple images into a single PDF
result = process(
    inputs=[Path("scan_001.jpg"), Path("scan_002.jpg"), Path("scan_003.jpg")],
    output_path=Path("combined_scans.pdf"),
)
```

### File Type Detection

```python
from pathlib import Path
from n1r.parser_gate import detect_file_type

# Detect file type before processing
detection = detect_file_type(Path("document.pdf"))
print(f"Type: {detection.detected_type} ({detection.confidence:.0%})")
print(f"Category: {detection.category}")  # 'pdf', 'image', 'office', or 'text'
```

### Pre-flight Validation

```python
from pathlib import Path
from n1r.parser_gate import validate_only

# Check if a file can be processed before conversion
validation = validate_only([Path("mystery_file.bin")])

if validation.can_process:
    print(f"File is {validation.detected_type}, ready to process")
else:
    print(f"Cannot process: {validation.issues}")
```

### Error Handling

```python
from pathlib import Path
from n1r.parser_gate import process
from n1r.parser_gate.exceptions import (
    ParserGateError,
    DetectionError,
    ConversionError,
    EncryptedPDFError,
)

try:
    result = process(
        inputs=[Path("document.docx")],
        output_path=Path("output.pdf"),
    )
except DetectionError as e:
    print(f"Could not identify file type: {e}")
except EncryptedPDFError as e:
    print(f"PDF is password-protected: {e}")
except ConversionError as e:
    print(f"Conversion failed: {e}")
except ParserGateError as e:
    print(f"Processing failed: {e}")
```

## API Reference

### Main Functions

#### `process(inputs, output_path) -> ProcessResult`

Process input files and produce a validated PDF.

| Parameter | Type | Description |
|-----------|------|-------------|
| `inputs` | `list[Path]` | List of input file paths |
| `output_path` | `Path` | Destination path for the output PDF |

**Returns**: `ProcessResult` with conversion details.

**Raises**: `DetectionError`, `UnsupportedTypeError`, `ConversionError`, `ValidationError`, `EncryptedPDFError`

#### `detect_file_type(source, filename) -> DetectionResult`

Detect the type of a file using Magika.

| Parameter | Type | Description |
|-----------|------|-------------|
| `source` | `Path \| BinaryIO` | File path or binary stream to detect |
| `filename` | `str \| None` | Required for BinaryIO inputs; filename with extension |

**Returns**: `DetectionResult` with type information.

**Raises**: `DetectionError`, `UnsupportedTypeError`, `TypeError`

#### `validate_only(inputs) -> ValidationResult`

Validate files without performing conversion.

| Parameter | Type | Description |
|-----------|------|-------------|
| `inputs` | `list[Path]` | List of input file paths to validate |

**Returns**: `ValidationResult` with validation details.

### Individual Converters

Available in `n1r.parser_gate.converters`:

```python
from n1r.parser_gate.converters import (
    images_to_pdf,   # Convert images to PDF
    office_to_pdf,   # Convert Office docs (DOCX, PPTX) and TXT to PDF
)
```

### Result Types

#### `DetectionResult`

```python
@dataclass
class DetectionResult:
    detected_type: str       # Magika-detected file type label
    confidence: float        # Confidence score (0.0-1.0)
    category: str            # 'pdf', 'image', 'office', or 'text'
    is_supported: bool       # Whether the type is supported by ParserGate
```

#### `ProcessResult`

```python
@dataclass
class ProcessResult:
    output_path: Path | None         # Path to the generated PDF (None if return_bytes only)
    output_bytes: bytes | None       # PDF bytes (None if return_bytes=False)
    detected_type: str               # Magika-detected file type label
    confidence: float                # Confidence score (0.0-1.0)
    validation_status: str           # "valid" or description of validation state
    warnings: list[str]              # Non-fatal warnings
    original_inputs: list[Path | BinaryIO]  # Input files/streams that were processed
```

#### `ValidationResult`

```python
@dataclass
class ValidationResult:
    is_valid: bool           # Whether validation passed
    detected_type: str       # Magika-detected file type label
    confidence: float        # Confidence score (0.0-1.0)
    can_process: bool        # True if ParserGate can handle this type
    issues: list[str]        # Any detected problems
```

### Exception Hierarchy

```
ParserGateError (base)
├── DetectionError
│   └── UnsupportedTypeError
├── ConversionError
└── ValidationError
    └── EncryptedPDFError
```

| Exception | When Raised |
|-----------|-------------|
| `DetectionError` | File type detection failed or confidence below 80% |
| `UnsupportedTypeError` | Detected file type is not supported |
| `ConversionError` | Conversion to PDF failed |
| `ValidationError` | Output PDF failed validation |
| `EncryptedPDFError` | PDF is encrypted or password-protected |

## Supported File Types

| Category | Formats | Conversion Method |
|----------|---------|-------------------|
| PDF | `.pdf` | Passthrough (validated) |
| Images | JPEG, PNG, TIFF, BMP, WebP | img2pdf |
| Office | DOCX, PPTX | LibreOffice headless |
| Text | TXT | LibreOffice headless |

### Batch Processing Rules

- **Images**: Multiple images can be merged into a single multi-page PDF (sorted alphanumerically)
- **Office/Text**: Must be processed individually (not batched)
- **PDF**: Single file passthrough only

## Requirements

### Python

- Python 3.10 or higher

### Python Dependencies (auto-installed)

| Package | Purpose |
|---------|---------|
| magika | File type detection |
| pypdf | PDF validation |
| img2pdf | Image to PDF conversion |
| Pillow | Image processing (EXIF, rotation) |
| charset-normalizer | Text encoding detection |
| loguru | Logging |

### External

| Tool | Purpose |
|------|---------|
| LibreOffice | Office document and text file conversion |

## Future Work

The following features are planned for future releases:

- **HTML Input Support**: Convert HTML documents to PDF (requires rendering strategy decision)
- **Encrypted PDF Support**: Password input for protected PDFs
- **Document Policy Rules**: Configurable limits for max pages, max file size, and allowed types
- **Document Classification**: Automatic routing based on document content/type
