extract_pptx
Extract slides, speaker notes, and comments from a PowerPoint (.pptx) deck.
Parameters
- pathstringone of
pathorsource_key - File path to the
.pptxfile; supports${ctx.key}interpolation. - source_keystringone of
pathorsource_key - Context key containing a file path, artifact URI, or artifact descriptor.
- formatstringdefault "text"
- Output format:
"text"(flattened slide text),"markdown"(one section per slide), or"json"(structured IR). - output_keystringdefault "content"
- Context key where the extracted output is stored. For
text/markdownthe value is a string; forjsonit is an object. - metadata_keystring
- If set, deck metadata (slide count + Dublin Core fields) is stored under this context key.
- comments_keystring
- If set, slide comments (from
ppt/comments/comment*.xmlplus author lookup inppt/commentAuthors.xml) are stored under this context key as a flat array. Comments are also attached per-slide in the JSON output. - media_modestringdefault "none"
- Embedded-media handling:
"none"keeps relationship metadata only;"artifact"streams each resolved image to the disk-backed artifact store and adds anartifactdescriptor. Artifact mode requiresformat = "json". - include_image_bytesbooleandefault false
- Deprecated compatibility input.
falseis accepted;trueis rejected with a migration error because extraction no longer materializes inline Base64 media.
Providing both
pathandsource_keyis an error.formataccepts"text","markdown", or"json". Artifact inputs are opened and SHA-256 verified inside the tracked blocking worker; OOXML parsing consumes that same rewound handle rather than a resolved store pathname. Presentformat,media_mode,output_key,metadata_key, andcomments_keyvalues must be strings. The deprecatedinclude_image_bytesinput accepts a native boolean or an interpolated boolean spelling (true/false,yes/no,on/off, or1/0, case-insensitive). Present values of the wrong type are rejected instead of being treated as absent.output_key,metadata_key, andcomments_keymust be pairwise distinct when the optional keys are set.media_mode = "artifact"is rejected unlessformat = "json".
Context Output
<output_key>(defaultcontent) — slides as text / Markdown / JSON IR.<metadata_key>(when set) — object withslide_countand (when present)title,author,subject,description,keywords,last_modified_by,created,modified,revision,category.<comments_key>(when set) — flat array ofPptxComment(see below).
JSON output shape
{
"slides": [
{
"slide_index": 1,
"title": "STIMULUS 1A",
"elements": [
{
"type": "image",
"alt_text": "Clinical scan",
"embed_id": "rId3",
"embedded_path": "ppt/media/image1.png",
"artifact": {
"artifact_uri": "artifact://sha256/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"size_bytes": 184322,
"mime_type": "image/png"
}
}
],
"speaker_notes": "Moderator: probe on rationale per group.",
"comments": [
{
"slide_index": 1,
"idx": "1",
"author_id": "0",
"author": "Reviewer A",
"initials": "RA",
"date": "2026-04-02T14:20:00",
"text": "Make sure to clarify 'group' wording."
}
]
},
{
"slide_index": 2,
"title": "Patient Profile A",
"elements": [
{
"type": "table",
"rows": [
["Field", "Value"],
["Age", "75"],
["Diagnosis", "Bilateral GA, juxtafoveal"]
]
}
]
}
]
}
Element types
text_block— a non-title shape's text.placeholdercarries the OOXML placeholder type (subTitle,body, etc.) when present and is omitted otherwise.paragraphs[]carrytextand optionallist_level(0-based indent for bulleted items).table— rendered asrows: [[string]].image— picture shape metadata.alt_textandembed_idare included when available.embedded_pathis included only when that ID resolves through an internal OOXML image relationship. Withmedia_mode = "artifact", a resolved embedded image also has anartifactobject containing its canonical URI, SHA-256 digest, decoded byte size, and MIME type. Binary bytes are not placed inNodeOutput.
The slide's title is taken from the placeholder with type="title" or type="ctrTitle". If no title placeholder exists, the title field is omitted.
Comments
The node currently parses legacy comments (ppt/comments/comment*.xml, indexed by slide number) and the matching ppt/commentAuthors.xml. PowerPoint's newer "modern comments" format (ppt/modernComments/) is not yet supported.
slide_index on each comment is derived from the comment file's numeric suffix (comment3.xml → slide 3).
Resource and cancellation contract
- The input must be a regular file. Its raw size and central directory are
bounded by
IRONFLOW_MAX_FILE_BYTES(default52428800, 50 MiB) before the ZIP library allocates archive metadata. On Unix and Windows, IronFlow also refuses to follow a final path-component symlink/reparse point; other platforms enforce the opened-handle regular-file check. - The EOCD/ZIP64 preflight validates central-directory bounds and
IRONFLOW_MAX_ZIP_ENTRIES(default10000). IronFlow then rejects duplicate part names, symlink or special-file parts, and a cumulative declared uncompressed size aboveIRONFLOW_MAX_ZIP_UNCOMPRESSED_BYTES(default536870912, 512 MiB). - XML parts are parsed directly from bounded ZIP-entry readers; IronFlow does
not first materialize a complete part as a byte vector or string. Each part
is capped by the smaller of the remaining cumulative ZIP budget and the
extraction-output budget, and actual decoded bytes accumulate across every
part read. All read, UTF-8, and XML parse errors in present slide, notes,
relationship, comment, author, and core-properties parts propagate.
Genuinely missing optional parts remain absent or empty; errors and limit
breaches never become partial output.
quick_xmlstill buffers its current event, so one unusually large text or CDATA token can require memory proportional to that token, but not to the complete XML part. IRONFLOW_MAX_EXTRACT_ITEMS(default250000) is shared across slide and comment archive parts, slide/relationship/content-type XML events, content-type definitions, slides, elements, paragraphs, table rows and cells, relationships, comments and authors, metadata fields, and embedded media occurrences. Content-type work occurs only when artifact media is requested.IRONFLOW_MAX_EXTRACT_OUTPUT_BYTES(default52428800, 50 MiB) bounds retained fields and formatting work and, authoritatively, the complete serializedNodeOutput. That final result includes the slide output, metadata, and the optional flat comments copy. This is a logical result limit, not a process-RSS limit and not the laterIRONFLOW_MAX_TASK_OUTPUT_BYTESpersistence limit.- Embedded media is decoded only when
format = "json"andmedia_mode = "artifact". It is copied from the ZIP entry to a private artifact-store staging file in bounded chunks while cancellation, cumulative decoded ZIP bytes, and the per-part limit are enforced. The bytes are hashed during that copy and published atomically underIRONFLOW_ARTIFACT_DIR(defaultdata/artifacts) assha256/<digest>. Repeated references to the same normalized package part reuse one descriptor and one stored file. Relationship attributes are XML decoded before use, duplicate relationship IDs are rejected, and only the transitional or strict OOXMLimagerelationship type is eligible forembedded_pathor artifact publication. Other relationship types and external targets are ignored and never opened or fetched. A recognized internal image relationship whose media part is missing is an error. Relationship elements must contain non-emptyId,Type, andTargetattributes; an unknownTargetModeis rejected instead of being treated as internal. Internal image targets must be relative package-part paths without URI schemes, queries, fragments, empty segments, or terminal dot segments. - In artifact mode, the descriptor MIME type comes from the matching
[Content_Types].xmloverride or extension default when one is declared; both lookups follow the package's ASCII case-insensitive matching rules and an override takes precedence. IronFlow falls back to its built-in image-extension mapping when the package omits a matching declaration, and toapplication/octet-streamwhen neither source recognizes the extension. MIME content is not inferred from the binary payload. A present malformed content-types part is an extraction error. - Artifact descriptors are small JSON values; the media itself is never Base64-encoded or retained in the extraction model. Artifacts are immutable and content-addressed. The local backend does not automatically expire or garbage-collect them, so operators own retention. A recovered or multi-host workflow must mount the same artifact directory on every worker; Redis or PostgreSQL context persistence does not copy local artifact files. Artifact publication is not rolled back if a later slide or output check fails, so a retention sweep must also reclaim unreferenced files.
- ZIP and XML work runs on a tracked blocking worker. Archive reads and parser loops check cancellation and the step/run deadline between chunks and XML events; synchronous ZIP-library construction cannot be interrupted inside the call and is bracketed by checkpoints. Task and run admission remain occupied until the physical worker stops.
Examples
Extract a stimulus deck as JSON
flow:step("extract_deck", nodes.extract_pptx({
path = "${ctx.deck_path}",
format = "json",
media_mode = "artifact",
output_key = "deck",
comments_key = "deck_comments",
metadata_key = "deck_meta"
}))
flow:step("show", nodes.log({
message = "Deck '${ctx.deck_meta.title}' has ${ctx.deck_meta.slide_count} slides"
})):depends_on("extract_deck")
Markdown for previewing
flow:step("preview", nodes.extract_pptx({
path = "/data/stimulus.pptx",
format = "markdown",
output_key = "md"
}))
Produces one ## Slide N section per slide with the title as an ### header, body paragraphs (bullets if list-leveled), tables as Markdown pipe tables, and speaker notes appended.