Conversion
The viewer renders PDFs natively. Every other format converts to PDF
through the @trupdf/converter service — a self-hosted container
built around the TruPDF conversion engine, with an office pipeline,
an HTML render pipeline, a raster pipeline, and the TruCAD engine
for 2D CAD.
Supported formats
| Family | Extensions | Pipeline |
|---|---|---|
.pdf | Passthrough — rendered natively, no server | |
| Word processing | .doc, .docx, .docm, .dot, .dotx, .dotm, .odt, .ott, .fodt, .rtf, .wps, .wpd, .pages, .abw, .sxw, .stw | Office pipeline |
| Spreadsheets | .xls, .xlsx, .xlsm, .xlsb, .xlt, .xltx, .xltm, .xlw, .ods, .ots, .fods, .csv, .tsv, .numbers, .dif, .slk, .dbf, .sxc, .stc | Office pipeline |
| Presentations | .ppt, .pptx, .pptm, .pot, .potx, .potm, .pps, .ppsx, .ppsm, .odp, .otp, .fodp, .key, .sxi, .sti | Office pipeline |
| Drawings & pub | .vsd, .vsdx, .vsdm, .vdx, .pub, .odg, .otg, .fodg, .std, .sxd, .epub, .eps | Office pipeline |
| HTML | .html, .htm, .xhtml | HTML render pipeline |
| Text & code | .txt; Markdown (.md, .mdown, .mkd, .mkdn, .mdx); .json, .xml, .xaml, .js, .ts, .yaml, .toml, .py, .c, C++, .java, .go, Rust, Ruby, shell, .sql | Text pipeline |
| Images | .jpg, .jpeg, .jfif, .jpe, .png, .apng, .gif, .bmp, .svg, .webp, .avif, .ico, .cur | Image pipeline (browser-native ones also client-side) |
| Extended raster | TIFF (multi-page), HEIC, camera raw | Raster pipeline |
.eml, .msg | Parsed, then rendered | |
| XPS | .xps, .oxps | XPS pipeline |
| XOD | .xod — legacy WebViewer XOD archives | XOD pipeline |
| 2D CAD | .dxf, .dwg (R12–R2018, CTB plot styles), .dwf, .dwfx, .dgn | TruCAD engine (sidecar service) |
| RVT / RFA | .rvt, .rfa | Rejected with a typed, actionable error — Revit files are 3D BIM, not 2D CAD |
2D CAD — the TruCAD engine
DXF, DWG, DWF, DWFx, and DGN drawings convert through TruCAD — TruenoTech's own clean-room CAD engine, deployed as a separate sidecar service the converter reaches over HTTP. DWG coverage spans releases R12 through R2018, including CTB plot-style tables so line weights and plot colours reproduce faithfully.
Because TruCAD is built and maintained in-house, CAD support carries no third-party licensing constraints and evolves with the product — new releases and entity types land in the sidecar without touching your integration.
Client-only flows
Two inputs never need the server:
- PDF — rendered natively by the viewer.
- Browser-native images (PNG, JPEG, GIF, WebP, and any other
format the browser decodes) — wrapped into a PDF client-side via
imageToPdf(bytes, mime).
Everything else requires the conversion service.
Running the service
docker-compose up -d brings up the conversion engine, the result
cache, and the converter orchestrator (port 3002). Internal service
wiring between the containers is preconfigured by the bundled
docker-compose.yml — you only set the variables documented in
Advanced setup (license, API key, CORS
origins, storage adapter).
Point the viewer at the converter:
NEXT_PUBLIC_CONVERTER_URL=https://convert.example.com # your deployment — no default<DocViewer
source={file}
converterUrl={process.env.NEXT_PUBLIC_CONVERTER_URL}
converterApiKey={apiKey} // sent as X-TruPDF-API-Key
…
/>The viewer auto-detects non-PDF sources, POSTs them to /convert,
then loads the resulting PDF from the returned URL. Long-running
conversions can use the async pair (/convert/async +
/convert/jobs/:id) instead.
HTTP API
| Route | Method | Purpose |
|---|---|---|
/health | GET | Liveness probe; reports whether the async queue is enabled |
/convert | POST | Multipart file; blocks until done, returns { url, hash, strategy, fromCache, durationMs, bytes } |
/convert/async | POST | Multipart file; returns 202 { jobId } immediately (503 when the queue is disabled) |
/convert/jobs/:id | GET | Job snapshot { id, status, progress, result, error, attemptsMade } |
/convert/jobs/:id | DELETE | Cancel / remove the job (204) |
/docgen | POST | Multipart template + data JSON → rendered PDF (same response shape as /convert) |
/bookmarks/read | POST | Read user bookmarks stored as a PDF attachment |
/bookmarks/write | POST | Write the bookmark tree back into the PDF attachment |
/fetch-url | POST | { url } — SSRF-guarded server-side fetch for CORS-blocked remote files; original filename surfaced via X-TruPDF-Original-Name |
Authentication — set TRUPDF_CONVERTER_API_KEY on the service
(required in production); callers send it as the X-TruPDF-API-Key
header. The viewer does this automatically from the converterApiKey
prop.
Password-protected documents — supply the open password as a
multipart password field or the X-Document-Password header on
/convert and /convert/async. Encrypted files without a valid
password fail with PASSWORD_REQUIRED.
Error codes
Failures return a typed JSON body { code, message }:
| Code | HTTP | When |
|---|---|---|
INVALID_INPUT | 400 | Missing / malformed field, bad URL, invalid JSON |
UNSUPPORTED_FORMAT | 400 | No pipeline claims the file's format |
PASSWORD_REQUIRED | 401 | Encrypted document; no or wrong password supplied |
NOT_FOUND | 404 | Unknown job id |
FILE_TOO_LARGE | 413 | Upload exceeds MAX_UPLOAD_BYTES |
QUEUE_DISABLED | 503 | Async endpoints called without a queue configured |
UPSTREAM_ERROR | 502 | /fetch-url upstream failure |
TIMEOUT | 504 | Conversion exceeded CONVERSION_TIMEOUT_MS |
INTERNAL | 500 | Everything else |
A conversion pipeline that is unreachable returns 503, and one that
rejects the input returns 502 — both with a typed code identifying
which pipeline failed (e.g. CAD_CONVERTER_UNAVAILABLE /
CAD_CONVERTER_FAILED for the TruCAD sidecar).
Strategy router
Each format family maps to a typed strategy:
PassthroughStrategy— PDFOfficeStrategy— word processing / spreadsheets / presentations / drawingsHTMLStrategy— HTML / XHTML / HTMTextStrategy— plain text, Markdown, code filesImageStrategy— browser-common raster imagesRasterImageStrategy— multi-page TIFFMagickImageStrategy— extended raster (HEIC, SVG rasterisation, camera raw)EmailStrategy— EML / MSGXpsStrategy— XPS / OXPSXodStrategy— legacy WebViewer XOD archivesTruCadStrategy— 2D CAD via the TruCAD sidecarRvtStrategy— typed rejection for Revit 3D BIM files
Adding a new format = adding a new strategy. No parallel pipelines.
Storage adapters
The converter persists results via a pluggable backend:
| Adapter | Env STORAGE_ADAPTER | Use case |
|---|---|---|
LocalStorageAdapter | local | Dev / single-node |
S3StorageAdapter | s3 | AWS S3 / MinIO / Cloudflare R2 |
AzureBlobStorageAdapter | azure | Azure Blob Storage |
WebhookStorageAdapter | webhook | Custom HTTP sink (HMAC-signed) |
See Advanced setup for the env-var matrix per adapter.
Result cache
Converted output lands in the result cache (7-day TTL by default,
CACHE_TTL_SECONDS). The cache key is sha256(input), so
re-uploading the same file returns the cached result instantly —
across users. fromCache: true in the response marks a hit.
Set LOG_LEVEL=debug to see cache hits / misses.
License gating
The converter constructor calls gate.require('convert'). The viewer
silently skips conversion endpoints when the bound license is missing
the feature. Trial tokens do not include convert; pro and
enterprise include it by default.