Document / PDF Node
The Document node has two modes:
parse-- PDF in, structured rows out. Each input row that carries a PDF payload produces one output row with{ name, source, pageCount, text, meta, ai_ready }.render-- template + rows in, PDF blob out. One output row per input row:{ name, source, pdfBase64, sizeBytes, pages, blobRef }.
Both modes optionally read from and write to a file connection (S3, Drive, SharePoint, Dropbox) via a resolver DI'd into the runtime executor.
When to use
- You need to pull structured content out of PDFs (invoices, contracts, resumes) so a downstream AI or Compute node can act on it.
- You need to generate branded PDFs from row data (receipts, statements, work orders) and either return them inline as base64 or upload them to a file connection.
- Both directions land in one node type so a "process an inbound PDF and emit a branded confirmation PDF" flow is two Document nodes chained end to end.
Parse mode
Real text extraction uses pdfjs-dist in its legacy build so no canvas native dependency is needed. Font hydration is disabled -- only text extraction runs -- which is why the runtime logs a harmless standardFontDataUrl warning per call.
Config
| Field | Default | Meaning |
|---|---|---|
mode | parse | |
pdfSourceColumn | pdfBuffer | Row column carrying the PDF payload (Buffer, Uint8Array, or base64 string). |
sourceConnectionId | null | (Optional) When rows carry sourceConnectionId + sourcePath, the executor fetches each PDF from the file connection before extraction. |
Input row shape (parse)
| Field | Notes |
|---|---|
pdfBuffer | Node Buffer or Uint8Array of the raw PDF bytes. |
pdfBase64 | Base64-encoded string of the raw PDF bytes. |
sourceConnectionId | (Optional) File-connection ID to fetch the PDF from. |
sourcePath | (Required with sourceConnectionId) Path inside the connection. |
name, source | Passed through to the output row. |
Output row shape (parse)
| Column | Type | Notes |
|---|---|---|
name | string | null | Passed through. |
source | string | null | Passed through (or the fetched sourcePath when the file-connection resolver was used). |
pageCount | number | Number of pages in the PDF. |
text | string | Extracted text content, page-joined by \n. |
meta | object | null | { byteLength, producer, creator, title } where available. |
ai_ready | boolean | Marker for AI-node chaining. Currently false (raw text only). |
Render mode
Two rendering paths are available:
Text templates (in-process)
Uses pdf-lib inside the runtime process. Each input row is rendered onto an A4 page with a Helvetica font. Template tokens are {{ column }} -- dotted paths are supported, missing columns become empty strings. Fast, cheap, no external service.
HTML templates (Puppeteer sidecar)
templateType: "html" forwards the rendered HTML to a Puppeteer sidecar at DLR_PDF_SIDECAR_URL. The sidecar's POST /render endpoint accepts { html } and returns the raw PDF bytes. Use this when you need real typography, brand assets, CSS grids, or embedded images.
When templateType: "html" is set but no sidecar is configured, the row lands on the error port with error: "html_renderer_not_configured" -- a clear signal to point at a sidecar or drop back to templateType: "text".
Config
| Field | Default | Meaning |
|---|---|---|
mode | Set to render. | |
template | Template string. {{ column }} tokens substituted per row. | |
templateType | text | text -> pdf-lib in-process. html -> Puppeteer sidecar. |
targetConnectionId | null | (Optional) File-connection ID to upload the rendered PDF to. |
targetPath | (Required with targetConnectionId) Path template (also supports {{ column }} tokens). |
Output row shape (render)
| Column | Notes |
|---|---|
name | Passed through, or document_<n>.pdf when the row didn't set one. |
source | Passed through. |
pdfBase64 | Base64-encoded raw PDF bytes. Downstream notification nodes can attach this directly. |
sizeBytes | Uploaded size (falls back to raw byte length when no upload happens). |
pages | Reserved for a future refinement. Currently null. |
blobRef | String returned by the file-connection resolver when the target was configured. null otherwise. |
Sidecar env vars
| Var | Default | Meaning |
|---|---|---|
DLR_PDF_SIDECAR_URL | (unset) | Base URL of the Puppeteer sidecar. Presence toggles HTML render on. |
DLR_PDF_SIDECAR_TIMEOUT_MS | 30000 | Per-render HTTP timeout. |
Failure modes
| Reason | Where the row lands |
|---|---|
Row has neither pdfBuffer nor pdfBase64 and no sourceConnectionId + sourcePath | error port with error: "no_pdf_payload" |
| Row asks for a file connection but no resolver is wired | error port with error: "file_resolver_not_configured" |
| Resolver throws | error port with the fetch/upload error message (capped at 500 chars) |
| Extractor throws (parse) or renderer throws (render) | error port with the underlying message |
mode: "render" with an empty template | error port with error: "missing_template" |
mode: "render" + templateType: "html" but no sidecar wired | error port with error: "html_renderer_not_configured" |
Any other mode value | error port with error: "unsupported_mode:<mode>" |
Inspector UI
The right-rail Document inspector exposes:
- Mode tiles (
Parse/Render). - Parse: PDF source column + optional file-connection source config.
- Render: template textarea + template-type tiles (
Text/HTML) + substitution preview toggle + file-connection target config.
Example flow fixture
qa/fixtures/document-node-example-flow.json shows two complete chains:
Source -> Document (parse) -> AI (extract) -> Load-- the classic "intelligent document processing" pattern.Source -> Document (render) -> Notification-- generate branded receipts and email the blob-ref link.