An OFD file previewer that serves both backend and frontend in one NestJS application. Provides API endpoints to fetch metadata, render pages as SVG/PNG/PDF, optional upload, and a lightweight viewer page with toolbar.
This implementation focuses on safety and simplicity while providing a working path to view typical text-only OFD pages. It includes a minimal sample OFD file generated on startup at /data/sample.ofd.
- Preview OFD via browser: http://127.0.0.1:3000/url?file=/data/sample.ofd or http://127.0.0.1:3000/url?file=https://example.com/sample.ofd
- Viewer UI rebuilt with React 18 + Ant Design 5 (served statically via CDN) with responsive layout and Ant Design components
- APIs
- GET
/api/ofd/metadata?file=...— pages, size (mm), title, author, creation date, extractable text flag, negotiated capabilities, active renderer - GET
/api/ofd/page?file=...&page=<n>&format=svg|png|pdf— render a page as SVG/PNG/PDF - GET
/api/ofd/text?file=...&page=<n>— extract text positions for a page (if possible) - POST
/api/upload— upload OFD and get a temporaryidto reference asfile=id:<id> - GET
/api/ofd/raw?file=...— download the original OFD
- GET
- Frontend
/urlpage- Ant Design toolbar with zoom, pagination, goto page, download current page (PNG/PDF), download original OFD, toggle text selection
- Keyboard shortcuts:
+zoom in,-zoom out,0reset, arrow keys turn pages - Remote/local file input with history update, loading indicators, inline error feedback via Ant Design
message/Alert
- Caching: in-memory LRU for parsed docs and rendered SVG pages (TTL 10 min)
- Security: path is restricted to OFD_ROOT or upload ids; prevents path traversal; file size limits
- Limits: file size, parse timeout, helpful error codes
src/ofd/parser.ts now orchestrates a chain of rendering strategies so that the backend can adapt to different levels of OFD complexity without code changes:
- OFDRW CLI strategy (optional). When the environment variable
OFDRW_CLIpoints to a binary or script that understands the expected CLI contract, the service delegates metadata extraction and page rendering to it. This is ideal for wrappers around ofdrw or any other robust renderer and unlocks support for complex graphics, embedded fonts, annotations, and electronic signatures. - Basic XML fallback. If no CLI is configured (or the CLI becomes unavailable), the built-in TypeScript strategy parses the OFD package directly and renders TextObject/TextCode instructions into SVG. This preserves the previous behaviour and keeps the application self-contained.
The parser automatically uses the first available strategy and falls back when a strategy errors out. Metadata responses now return a capabilities object describing the negotiated features (text, vector drawing, images, annotations, signatures) so the frontend can react accordingly.
When only the basic strategy is active, the following limitations remain:
- Complex graphics (paths, images, clipping, CTM transforms) are not drawn
- Fonts defined in resource packages are not embedded; system fallbacks are used
- Advanced layout features such as vertical text, kerning, or glyph substitutions are ignored
- Annotations, stamps, and e-seals are not rendered
- Multi-document OFD packages remain unsupported
Configure an OFDRW-compatible CLI to unlock the advanced features required for production workloads.
The backend transparently downloads HTTP/HTTPS OFD files, validates their size, and caches them under OFD_ROOT/remote-cache. Cache lifetime and download timeout can be tuned with OFD_REMOTE_CACHE_TTL and OFD_REMOTE_TIMEOUT. Metadata, page rendering, and raw download endpoints accept remote URLs directly, enabling /url?file=https://example.com/sample.ofd style previews without preprocessing.
Requirements: Node.js 18+.
- Install dependencies
npm install- Run in development
npm run start:devThis will start the server at http://127.0.0.1:3000 and auto-generate a sample OFD at /data/sample.ofd.
Open the viewer:
- http://127.0.0.1:3000/url?file=/data/sample.ofd
- or http://127.0.0.1:3000/url?file=sample.ofd (relative to OFD_ROOT)
- Build & run production
npm run build
npm run start:prodPORT— server port (default 3000)OFD_ROOT— the root directory for OFD files (default/data). Absolute paths are only allowed if inside this directory. The sample file is created here.MAX_OFD_SIZE— maximum OFD size in bytes (default 104857600, i.e., 100 MiB)OFDRW_CLI— optional path to an executable or script that implements the OFDRW-compatible CLI used byOfdServiceOFDRW_TIMEOUT— override the CLI invocation timeout in milliseconds (default 20000)OFDRW_DISABLE— set totrue/1/yesto skip the OFDRW strategy even whenOFDRW_CLIis configuredOFDRW_KEEP_ARTIFACTS— set to1to retain temporary working directories produced by the CLI for debuggingOFD_REMOTE_CACHE_TTL— cache duration for downloaded remote OFD files in milliseconds (default 300000)OFD_REMOTE_TIMEOUT— timeout for downloading remote OFD files in milliseconds (default 20000)
When OFDRW_CLI is configured the command is invoked with the following expectations:
OFDRW_CLI metadata <input.ofd>must write a JSON object tostdoutcontaining:{ "meta": { "pages": 1, "widthMM": 210, "heightMM": 297, "title": "...", "author": "...", "creationDate": "...", "textExtractable": true }, "capabilities": { "text": true, "vector": true, "images": true, "annotations": true, "signatures": true }, "pageRefs": ["page-1", "page-2"] }capabilitiesandpageRefsare optional; sensible defaults are applied when they are omitted.OFDRW_CLI render --page <n> --format svg --output <outputBase> <input.ofd>must create<outputBase>.svg. Optionally write<outputBase>.jsonwith an array of text items that matchPageTextItem.
Any script or binary that fulfils this contract can drive the advanced renderer.
POST /api/uploadwith a multipart form fieldfile- Response returns
{ id, file: 'id:<id>' } - Use that value as the
filequery parameter, e.g./url?file=id:<id>
- Metadata:
GET /api/ofd/metadata?file=/data/sample.ofd - First page SVG:
GET /api/ofd/page?file=/data/sample.ofd&page=1&format=svg - First page PNG:
GET /api/ofd/page?file=/data/sample.ofd&page=1&format=png - First page PDF:
GET /api/ofd/page?file=/data/sample.ofd&page=1&format=pdf - Text layer:
GET /api/ofd/text?file=/data/sample.ofd&page=1 - Raw download:
GET /api/ofd/raw?file=/data/sample.ofd
All endpoints enforce path restrictions to stay within OFD_ROOT.
Run tests:
npm testIncluded tests (at least 3):
- Metadata success for sample
- Page render success (SVG and PNG)
- Metadata error for non-existing file
A simple Dockerfile is provided. Build and run:
docker build -t ofd-viewer .
docker run --rm -p 3000:3000 -e OFD_ROOT=/data -v $(pwd)/data:/data ofd-viewerThe container will create /data/sample.ofd on first start if not present.
The strategy pipeline in src/ofd/parser.ts is intentionally open-ended:
- Point
OFDRW_CLIat any command that produces metadata JSON and per-page SVG (with optional text JSON) following the documented contract to gain full OFD compatibility via external renderers such as ofdrw. - Implement your own
OfdRenderingStrategy(for example, an HTTP bridge or a WASM renderer) and pass it tonew OfdParser({ strategies: [...] })insideOfdServiceif you need tighter control.
Unsupported features still surface readable errors and the viewer informs the user with options to retry or upload another file.