@rustors/react
v0.4.0
Published
React hooks, provider, and UI components for RustO! OCR engine powered by pure-Rust MNN (rusto-nn)
Maintainers
Readme
@rustors/react
Official React hooks, context provider, and UI components for RustO! — client-side WebAssembly OCR powered by @rustors/web, Indra OCR, and PaddleOCR.
Features
- Zero-Backend Privacy: Runs OCR in the browser via WebAssembly + Web Workers (
@rustors/web). - Standalone Model URLs: Load model files directly from
public/models/, your CDN, or Hugging Face Hub (byrizki/rusto-models) — no npm model wrapper packages required. - Indra OCR & PP-OCRv6 Presets: Supports
'indra'alongside'ppv6','ppv5','ppv4', and'ppv3', plus optionalunwarp,orientation,layout, andordermodels. - Hooks & Ready-Made Components:
<RustoProvider>,useOcr(),useRusto(),<OcrDropzone>,<OcrOverlay>, and direct fluentDetectTextBuilderaccess (.asLines().run(),.asWords().run(),.asSpatial().run()).
Installation
npm install @rustors/react @rustors/web
# or
pnpm add @rustors/react @rustors/webConfiguring Models with <RustoProvider>
Point models to standalone model URLs hosted in your app's public/models/ folder, a CDN, or Hugging Face Hub (https://huggingface.co/byrizki/rusto-models/resolve/main with CORS enabled):
import { RustoProvider } from '@rustors/react';
const HF_BASE = 'https://huggingface.co/byrizki/rusto-models/resolve/main';
export function App() {
return (
<RustoProvider
config={{
preset: 'indra', // or 'ppv6' for lightweight PP-OCRv6 Tiny
enableReadingOrder: true,
models: {
detection: `${HF_BASE}/indra_det.rten`,
recognition: `${HF_BASE}/indra_rec.rten`,
dictionary: `${HF_BASE}/indra_meta.json`,
// Optional auxiliary document models:
unwarp: `${HF_BASE}/uvdoc.rten`,
orientation: `${HF_BASE}/indra_orient.rten`,
layout: `${HF_BASE}/layout_cdla.rten`,
order: `${HF_BASE}/indra_order.rten`,
},
}}
>
<InvoiceScanner />
</RustoProvider>
);
}Using the useOcr Hook & UI Components
import { useState } from 'react';
import { OcrDropzone, OcrOverlay, useOcr, useRusto } from '@rustors/react';
export function InvoiceScanner() {
const { status } = useRusto();
const [imageUrl, setImageUrl] = useState<string | null>(null);
const { detect, results, spatialText, isProcessing, error } = useOcr({
autoUnwarp: true,
layoutReading: true,
output: 'lines',
});
const handleFile = async (file: File) => {
setImageUrl(URL.createObjectURL(file));
await detect(file);
};
if (status !== 'ready') {
return <p>Loading OCR engine ({status})...</p>;
}
return (
<div>
<OcrDropzone onFileAccepted={handleFile} disabled={isProcessing} />
{error && <p role="alert">{error.message}</p>}
{imageUrl && (
<div style={{ position: 'relative', display: 'inline-block' }}>
<img src={imageUrl} alt="Uploaded document" style={{ maxWidth: '100%' }} />
<OcrOverlay results={results} />
</div>
)}
<ul>
{results.map((item, idx) => (
<li key={idx}>
[{item.score.toFixed(2)}] ({item.layout_type ?? 'line'} #{item.region_id ?? '-'}){' '}
{item.text}
</li>
))}
</ul>
{spatialText && <pre>{spatialText}</pre>}
</div>
);
}Direct Fluent Builder Access (asLines / asWords / asSpatial)
When you want chained fluent builder methods directly inside a component or custom hook, access the underlying WebOcrEngine via useRusto() (or import detectText from @rustors/web):
import { useRusto } from '@rustors/react';
export function useFluentScanner() {
const { service } = useRusto();
const scanInvoice = async (file: File) => {
const engine = service.getEngine();
// Line-level structured results with document unwarping & layout reading order
const lines = await engine
.detectText(file)
.withAutoUnwarp()
.withLayoutReading()
.asLines()
.run();
// 2D spatial plain-text layout
const spatial = await engine
.detectText(file)
.asSpatial()
.run();
return { lines, spatial };
};
return { scanInvoice };
}Exported Utilities
- Hooks & Context:
RustoProvider,RustoContext,useRusto,useOcr - Components:
OcrDropzone,DropzonePrompt,DropzonePreview,OcrOverlay,BoundingBox - Formatters & Geometry:
resultsToText,resultsToCsv,resultsToJson,scaleFrame,scaleQuad,calculateScale
License
Apache-2.0 OR MIT
