Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .agents/skills/local-ocr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,29 +75,29 @@ Need to extract text from a PDF file or process multiple images as one document.

```bash
# Full PDF with JSON output
light-ocr document report.pdf --format json
light-ocr-document report.pdf --format json

# Page range with streaming JSONL
light-ocr document report.pdf --pages 1-10 --format jsonl
light-ocr-document report.pdf --pages 1-10 --format jsonl

# Multiple images as one document
light-ocr document scan1.png scan2.png --format text
light-ocr-document scan1.png scan2.png --format text

# Check if PDF support is available
light-ocr doctor --json
light-ocr-document info
```

PDF rendering uses `pdfium-native` (optional dependency). If unavailable, use image-only workflows or install `pdfium-native` separately.
The document command requires the explicit `@arcships/light-ocr-document@next` preview package. Its pinned `pdfium-native` dependency installs a verified platform prebuild; it is not part of the stable image package.

### System diagnostics

Need to check hardware, execution providers, or PDF support status.
Need to check hardware or execution provider status.

```bash
light-ocr doctor --json
```

Returns system info (Node.js version, OS, CPU, memory), native runtime status, available providers, and module availability. No user content is collected; hostname is SHA-256 hashed.
Returns system info (Node.js version, OS, CPU, memory), native runtime status, and available providers. No user content, hostname, username, path, or stable device identifier is collected.

## Decision flow

Expand All @@ -108,7 +108,7 @@ Need text from an image or document?
├── Need text + coordinates? → recognize --format json
├── Only need where text is? → detect
├── Large image, unsure where text is? → detect first, then recognize --region
├── PDF or multiple images? → document <files> --format json
├── PDF or multiple images? → light-ocr-document <files> --format json
├── Need system/hardware info? → doctor --json
└── Need engine info or version? → info --model-info / info --version
```
Expand Down
24 changes: 20 additions & 4 deletions .github/workflows/npm-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
version:
description: Core and stable Small facade version
required: true
default: 0.4.0
default: 0.5.4
type: string
publish_to_registry:
description: Publish the tested candidate to npm under next
Expand Down Expand Up @@ -255,7 +255,7 @@ jobs:
npm install --offline --ignore-scripts --no-audit --no-fund --package-lock=false \
../dist/release/arcships-light-ocr-model-ppocrv6-small-0.3.4.tgz \
../dist/release/arcships-light-ocr-${{ matrix.native }}-${RELEASE_VERSION}.tgz \
../dist/release/arcships-light-ocr-runtime-0.1.3.tgz \
../dist/release/arcships-light-ocr-runtime-0.1.4.tgz \
../dist/release/arcships-light-ocr-${RELEASE_VERSION}.tgz
cp ../tools/npm/smoke.cjs .
- name: Run stable Small OCR
Expand All @@ -264,6 +264,13 @@ jobs:
env:
LIGHT_OCR_SMOKE_FIXTURE: ${{ github.workspace }}/corpus/fixtures/generated-hello-123
run: node smoke.cjs
- name: Install and smoke the explicit Document preview
shell: bash
working-directory: package-smoke
run: |
npm install --no-audit --no-fund --package-lock=false \
../dist/release/arcships-light-ocr-document-0.1.0.tgz
node ../tools/npm/document-smoke.cjs
- name: Install and smoke preview tiers
if: matrix.preview
shell: bash
Expand All @@ -274,8 +281,8 @@ jobs:
npm install --offline --ignore-scripts --no-audit --no-fund --package-lock=false \
../dist/release/arcships-light-ocr-model-ppocrv6-tiny-0.1.0.tgz \
../dist/release/arcships-light-ocr-model-ppocrv6-medium-0.1.0.tgz \
../dist/release/arcships-light-ocr-tiny-0.1.2.tgz \
../dist/release/arcships-light-ocr-medium-0.1.2.tgz
../dist/release/arcships-light-ocr-tiny-0.1.3.tgz \
../dist/release/arcships-light-ocr-medium-0.1.3.tgz
node ../tools/npm/smoke-tier.cjs @arcships/light-ocr-tiny light-ocr-tiny \
ppocrv6-tiny-onnx-20260722.1
node ../tools/npm/smoke-tier.cjs @arcships/light-ocr-medium light-ocr-medium \
Expand Down Expand Up @@ -323,6 +330,15 @@ jobs:
--tarball-dir dist/release
--phase facade
--tag next
- name: Verify the published Document preview with install scripts
shell: bash
run: |
mkdir registry-document
cd registry-document
npm init --yes
npm install --no-audit --no-fund --package-lock=false \
"@arcships/light-ocr-document@0.1.0"
node ../tools/npm/document-smoke.cjs
- name: Verify the published stable package offline
shell: bash
run: |
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,29 @@ This file records user-visible changes to `light-ocr`. Published artifact detail

## [Unreleased]

## [0.5.4] - 2026-07-27

### Fixed

- Restored the stable facade to image OCR plus `doctor`; the unfinished
Document surface no longer changes the default package's offline,
no-install-script contract.
- Moved PDF and multi-page OCR to the explicit
`@arcships/light-ocr-document@next` preview and its
`light-ocr-document` command.
- Added real PDF rendering, DPI-aware pixel limits, cancellation, deterministic
cleanup, standalone CLI parsing, TypeScript declarations, and release smoke
coverage for the Document preview.
- Removed the stable hostname hash from voluntary diagnostics; `doctor` reports
no hostname or stable device identifier.
- Corrected README and release records that had described the next-only 0.5.3
candidate as a complete stable release.

## [0.5.3] - 2026-07-26

> Superseded next-channel candidate. It was not promoted as a complete stable
> release; see the 0.5.4 repair record.

### Fixed

- Fixed corrupted UTF-8 characters in tiny/medium package.json descriptions caused by encoding issues during version bump.
Expand Down
29 changes: 13 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ English | [简体中文](README.zh-CN.md)

**Fast, offline OCR for Node.js and C++.**

Recognize text in JPEG, PNG, PDF, or raw image data directly on your machine. `light-ocr` returns lines in reading order with confidence scores and quadrilateral coordinates. For Node.js, the npm package includes PP-OCRv6 Small and prebuilt components for macOS, Linux, and Windows.
Recognize text in JPEG, PNG, or raw image data directly on your machine. `light-ocr` returns lines in reading order with confidence scores and quadrilateral coordinates. For Node.js, the npm package includes PP-OCRv6 Small and prebuilt components for macOS, Linux, and Windows.

## Quick start

Expand Down Expand Up @@ -74,36 +74,33 @@ light-ocr recognize image.png --region 100,80,640,320 --format json
# Engine info
light-ocr info --version

# Process PDF or multiple images
light-ocr document report.pdf --format json
light-ocr document report.pdf --pages 1-5 --format jsonl
light-ocr document page1.png page2.png --format text

# System diagnostics (hardware, providers, PDF support)
# System diagnostics (hardware and providers)
light-ocr doctor --json
```

Five subcommands: `recognize` (default), `detect` (boxes only), `document` (PDF/multi-page), `info` (version diagnostics), `doctor` (system diagnostics). Output wraps in a versioned `schemaVersion: 1` envelope with stable line/detection IDs. EXIF orientation is corrected automatically. See the [CLI design](docs/cli-design.md) and [npm README](bindings/node/README.md#cli) for full reference.
Four subcommands: `recognize` (default), `detect` (boxes only), `info` (version diagnostics), and `doctor` (system diagnostics). Output wraps in a versioned `schemaVersion: 1` envelope with stable line/detection IDs. EXIF orientation is corrected automatically. See the [CLI design](docs/cli-design.md) and [npm README](bindings/node/README.md#cli) for full reference.

### PDF and multi-page documents

`light-ocr document` processes PDF files and multiple images in one call. PDF rendering uses [pdfium-native](https://www.npmjs.com/package/pdfium-native) (an optional dependency that auto-installs on supported platforms). If PDFium is unavailable, image-only document workflows still work.
PDF and multi-page OCR live in an explicit preview package so the stable default keeps its script-free, offline-installable dependency closure. Installing the preview runs `pdfium-native`'s verified prebuild installer; PDF processing itself stays local.

```bash
npm install @arcships/light-ocr-document@next

# Single PDF with default 150 DPI
light-ocr document report.pdf
light-ocr-document report.pdf

# Page range with streaming JSONL output
light-ocr document report.pdf --pages 1-10 --format jsonl
light-ocr-document report.pdf --pages 1-10 --format jsonl

# Multiple images as one document
light-ocr document scan1.png scan2.png scan3.png --format text
light-ocr-document scan1.png scan2.png scan3.png --format text
```

Programmatic API:

```ts
import { recognizeDocument } from "@arcships/light-ocr";
import { recognizeDocument } from "@arcships/light-ocr-document";

// Stream pages from a PDF
for await (const page of recognizeDocument("report.pdf", { dpi: 200 })) {
Expand All @@ -127,9 +124,9 @@ An [Agent Skill](.agents/skills/local-ocr/SKILL.md) is included for AI agents th

## What you get

- **Local processing.** Images, PDFs, and OCR results stay on your machine.
- **Local processing.** Images and OCR results stay on your machine; the explicit Document preview also processes PDFs locally.
- **One package to install.** The model and matching prebuilt component are included with the npm package.
- **PDF and multi-page support.** Process PDFs and multiple images with streaming output.
- **Opt-in document support.** The separate Document preview processes PDFs and multiple images with streaming output.
- **Useful output.** Every line includes recognized text, confidence, and its position in the original image.
- **Hardware acceleration by default.** Auto tries Core ML first on macOS 15+ Apple Silicon, and WebGPU first on the Linux and Windows builds below.
- **Application-friendly execution.** Recognition runs off the JavaScript main thread and supports queues, cancellation, and explicit cleanup.
Expand All @@ -150,7 +147,7 @@ The npm package provides the following six builds. The default `createEngine()`
| Windows x64 | WebGPU through D3D12, then CPU |
| Windows arm64 | CPU |

Applications that need explicit control can choose `auto`, `cpu`, `apple`, or `webgpu` through the [`execution` option](bindings/node/README.md#使用).
Applications that need explicit control can choose `auto`, `cpu`, `apple`, or `webgpu` through the [`execution` option](bindings/node/README.md#with-options).

## Measured performance

Expand Down
27 changes: 12 additions & 15 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

**面向 Node.js 与 C++ 的快速离线 OCR。**

直接在本机识别 JPEG、PNG、PDF 或像素数据,返回按阅读顺序排列的文字、置信度和四边形坐标。Node.js 用户安装的 npm 包内置 PP-OCRv6 Small 模型,并提供 macOS、Linux 和 Windows 的预编译组件。
直接在本机识别 JPEG、PNG 或像素数据,返回按阅读顺序排列的文字、置信度和四边形坐标。Node.js 用户安装的 npm 包内置 PP-OCRv6 Small 模型,并提供 macOS、Linux 和 Windows 的预编译组件。

## 快速开始

Expand Down Expand Up @@ -72,36 +72,33 @@ light-ocr recognize image.png --region 100,80,640,320 --format json
# 引擎信息
light-ocr info --version

# 处理 PDF 或多页图片
light-ocr document report.pdf --format json
light-ocr document report.pdf --pages 1-5 --format jsonl
light-ocr document page1.png page2.png --format text

# 系统诊断(硬件、加速器、PDF 支持)
# 系统诊断(硬件与加速器)
light-ocr doctor --json
```

五个子命令:`recognize`(默认)、`detect`(只检测框)、`document`(PDF/多页)、`info`(版本诊断)、`doctor`(系统诊断)。输出使用 `schemaVersion: 1` 版本化 envelope,带稳定 line/detection ID。EXIF 方向自动修正。完整参考见 [CLI 设计](docs/cli-design.md) 和 [npm README](bindings/node/README.md#cli)。
四个子命令:`recognize`(默认)、`detect`(只检测框)、`info`(版本诊断)、`doctor`(系统诊断)。输出使用 `schemaVersion: 1` 版本化 envelope,带稳定 line/detection ID。EXIF 方向自动修正。完整参考见 [CLI 设计](docs/cli-design.md) 和 [npm README](bindings/node/README.md#cli)。

### PDF 和多页文档

`light-ocr document` 一次调用处理 PDF 文件和多张图片。PDF 渲染使用 [pdfium-native](https://www.npmjs.com/package/pdfium-native)(可选依赖,支持平台会自动安装)。如果 PDFium 不可用,仅图片的文档流程仍然可用
PDF 和多页 OCR 位于显式安装的 Preview 包中,稳定默认包因此继续保持无安装脚本、可离线安装的依赖闭包。安装 Preview 时会运行 `pdfium-native` 的已校验 prebuild 安装器;实际 PDF 处理仍完全在本机完成

```bash
npm install @arcships/light-ocr-document@next

# 单个 PDF,默认 150 DPI
light-ocr document report.pdf
light-ocr-document report.pdf

# 指定页码范围,流式 JSONL 输出
light-ocr document report.pdf --pages 1-10 --format jsonl
light-ocr-document report.pdf --pages 1-10 --format jsonl

# 多张图片作为一个文档
light-ocr document scan1.png scan2.png scan3.png --format text
light-ocr-document scan1.png scan2.png scan3.png --format text
```

编程 API:

```ts
import { recognizeDocument } from "@arcships/light-ocr";
import { recognizeDocument } from "@arcships/light-ocr-document";

// 从 PDF 流式获取页面
for await (const page of recognizeDocument("report.pdf", { dpi: 200 })) {
Expand All @@ -125,9 +122,9 @@ for await (const page of recognizeDocument([buf1, buf2, buf3])) {

## 主要能力

- **本地处理。**图片、PDF 和 OCR 结果始终留在本机。
- **本地处理。**图片和 OCR 结果始终留在本机;显式安装的 Document Preview 也只在本机处理 PDF
- **只需安装一个包。**模型和当前平台的预编译组件会随 npm 包一起安装。
- **PDF 和多页支持。**处理 PDF 和多张图片,流式输出结果
- **按需安装文档能力。**独立 Document Preview 支持 PDF、多张图片和流式输出
- **直接得到可用结果。**每一行都包含识别文字、置信度和原图位置。
- **默认使用硬件加速。**Auto 在 macOS 15+ Apple Silicon 上优先使用 Core ML,在下表的 Linux 和 Windows 版本中优先使用 WebGPU。
- **适合应用内调用。**识别任务在 JavaScript 主线程之外执行,并支持队列、取消和明确释放资源。
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.3
0.5.4
4 changes: 4 additions & 0 deletions bindings/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ light-ocr image.png --format text
# Engine info
light-ocr info --version
light-ocr info --model-info

# Voluntary system/provider diagnostics
light-ocr doctor --json
```

### Subcommands
Expand All @@ -35,6 +38,7 @@ light-ocr info --model-info
| `recognize <path> [flags]` | Full OCR: detection + recognition (default) |
| `detect <path> [flags]` | Detection only: text region boxes, no recognition |
| `info --model-info \| --version` | Engine/version diagnostics, no image read |
| `doctor [--json]` | System/provider diagnostics; never reports a hostname or stable device ID |

`light-ocr image.png` without a subcommand is implicit `recognize`.

Expand Down
59 changes: 7 additions & 52 deletions bindings/node/test/cli.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ test('info: --version is metadata-only and reports the Small tier', async () =>
assert.equal(code, EXIT.success);
assert.equal(stderr, '');
const info = JSON.parse(stdout);
assert.equal(info.core, '0.5.0');
assert.equal(info.core, '0.5.4');
assert.equal(info.tier, 'small');
assert.equal(info.maturity, 'stable');
assert.equal(info.model, 'ppocrv6-small-native-20260719.1');
Expand Down Expand Up @@ -464,7 +464,7 @@ test('doctor: modules section reports runtime and model availability', async ()
const result = JSON.parse(stdout);
assert.equal(typeof result.modules.runtime, 'boolean');
assert.equal(typeof result.modules.model, 'boolean');
assert.equal(typeof result.modules.pdfium, 'boolean');
assert.equal('pdfium' in result.modules, false);
});

test('doctor: native section has status field', async () => {
Expand Down Expand Up @@ -506,59 +506,14 @@ test('doctor: top-level help includes doctor subcommand', async () => {
assert.match(stdout, /doctor/);
});

test('doctor: hostHash is 16-char hex and privacy-safe', async () => {
test('doctor: omits hostname-derived stable identifiers', async () => {
const { stdout } = await runCli(['doctor']);
const result = JSON.parse(stdout);
assert.match(result.system.hostHash, /^[a-f0-9]{16}$/,
'hostHash should be 16-char hex (privacy-safe hash)');
assert.equal(Object.hasOwn(result.system, 'hostHash'), false);
assert.equal(Object.hasOwn(result.system, 'hostname'), false);
});

// --- document subcommand tests ---

test('help: document subcommand prints flags', async () => {
const { code, stdout } = await runCli(['document', '--help']);
assert.equal(code, EXIT.success);
assert.match(stdout, /light-ocr document — process PDF or multiple images/);
assert.match(stdout, /--format json\|jsonl\|text/);
assert.match(stdout, /--pages N-M/);
assert.match(stdout, /--dpi <n>/);
assert.match(stdout, /--max-pages <n>/);
assert.match(stdout, /--quiet/);
assert.match(stdout, /--provider/);
});

test('help: top-level help includes document subcommand', async () => {
test('main CLI keeps document work in the independent preview command', async () => {
const { stdout } = await runCli(['--help']);
assert.match(stdout, /document.*Process PDF or multiple images/);
});

test('document: rejects no arguments exit 64', async () => {
const { code, stderr } = await runCli(['document']);
assert.equal(code, EXIT.usage);
assert.match(stderr, /expected a PDF or image file path/);
});

test('document: rejects invalid --format exit 65', async () => {
const { code, stderr } = await runCli(['document', 'x.png', '--format', 'csv']);
assert.equal(code, EXIT.invalid_argument);
assert.match(stderr, /unsupported --format/);
});

test('document: rejects invalid --pages exit 65', async () => {
const { code, stderr } = await runCli(['document', 'x.png', '--pages', 'abc']);
assert.equal(code, EXIT.invalid_argument);
assert.match(stderr, /--pages expects N or N-M/);
});

test('document: rejects unknown subcommand before file-not-found', async () => {
// document is a valid subcommand, so it processes its args
// invalid --format should be caught before file-not-found
const { code } = await runCli(['document', 'x.png', '--format', 'csv']);
assert.equal(code, EXIT.invalid_argument);
});

test('document: pdfium status reported in doctor', async () => {
const { stdout } = await runCli(['doctor', '--json']);
const result = JSON.parse(stdout);
assert.equal(typeof result.modules.pdfium, 'boolean');
assert.doesNotMatch(stdout, /document/);
});
Loading
Loading