> IronFlow documentation for v1.20.0. [HTML version](https://ironflow.work/docs/nodes/pdf_metadata) · [Release source](https://github.com/skitsanos/ironflow/blob/v1.20.0/docs/nodes/pdf_metadata.md)

# `pdf_metadata`

Extract metadata from a PDF file (document info dictionary + page count).

## Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `path` | string | one of `path` or `source_key` | — | PDF file path |
| `source_key` | string | one of `path` or `source_key` | — | Context key containing a file path, artifact URI, or artifact descriptor |
| `output_key` | string | no | `"metadata"` | Prefix for output key |

> Providing both `path` and `source_key` is an error.
> Artifact inputs are opened and SHA-256 verified inside the tracked blocking worker; PDF parsing consumes that same rewound handle rather than a resolved store pathname.

## Resource contract

The source must be a regular file and is capped by
`IRONFLOW_MAX_PDF_BYTES` (default 100 MiB) before and while it is read; a file
that grows after the initial metadata check cannot cross the limit. On Unix and
Windows the final path component may not be a symlink/reparse point. Loading and metadata traversal run on
a tracked blocking worker with cancellation checkpoints around and during input
reads. `lopdf` still constructs its document object model in memory, so the byte
ceiling bounds raw input rather than promising that parser RSS equals file size.

[Shared PDF loading](https://github.com/skitsanos/ironflow/blob/v1.20.0/docs/PDF_LOADING.md) additionally enforces
`IRONFLOW_MAX_PDF_DECOMPRESSED_STREAM_BYTES` (64 MiB) per object/cross-reference
stream and `IRONFLOW_MAX_PDF_OBJECTS` (250000) after parsing. Strict loading and
bounded recovery checks reject oversized streams instead of returning partial
metadata. These are independent limits, not a cumulative memory ceiling.

## Context Output

- `<output_key>` — object containing metadata:
  - `pages` — page count
  - `title`, `author`, `subject`, `keywords`, `creator`, `producer`, `created`, `modified` when present

## Example

```lua
local flow = Flow.new("pdf_metadata_demo")

flow:step("meta", nodes.pdf_metadata({
    path = "examples/fixtures/ironflow-sample.pdf",
    output_key = "pdf_meta"
}))

-- Interpolation does not evaluate fallback expressions, so normalize the
-- optional field in an explicit workflow step.
flow:step("metadata_defaults", function(ctx)
    return {
        pdf_creator = ctx.pdf_meta.creator or "unknown"
    }
end):depends_on("meta")

flow:step("log", nodes.log({
    message = "PDF has ${ctx.pdf_meta.pages} page(s), produced by ${ctx.pdf_creator}"
})):depends_on("metadata_defaults")

return flow
```
