npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@openpresentation/opf-pptx

v0.33.0

Published

Pure local OPF to PPTX export and PPTX to OPF import tooling.

Readme

OPF PPTX

Source prototype: toPptx(deck, {bodyText: "native"}) is an opt-in mode and stays a prototype. It writes one editable paragraph, flat-bullet or numbered-list body per text region (the first is the Body placeholder) and a wrapped plain title as one Title placeholder, with an explicit native-body-fallback diagnostic for anything it cannot represent; the default measured output is unchanged. Keynote 15.1.1 was checked for Title and Body controls and reflow after an edit (evidence); PowerPoint has not been checked, and nothing here claims native raster equivalence. See Choosing body text mode and the scope and qualification boundaries.

New since 0.22.0 (each section below says what changed and what is unchanged by default):

Catalogs (OPF 0.20): export resolves the document's embedded records and host-registered catalogs (Catalog[], never fetched). Register Gallery 3 explicitly for export: import { gallery } from "@openpresentation/gallery"; toPptx(deck, { catalogs: [gallery] }). Bare ids resolve in the document's custom/default records, then the registered default catalog; catalogs.default:false closes that fallback. When import catalogs is omitted, foreign PowerPoint layout type/name mapping uses the default catalog from installed @openpresentation/gallery; theme and colour-scheme recovery still requires explicitly registered catalogs; fromPptx(bytes, { catalogs: [] }) explicitly opts out. An unresolved reference emits core's unresolved-reference diagnostic and uses engine defaults; strictReferences:true refuses the export with OPFPptxError. Native layout tags preserve valid catalog sources and opt-outs, and damaged identity/source metadata emits diagnostics instead of creating invalid catalog groups.

Generated slide masters carry the display name pptx.gallery, including masters copied for per-slide script fonts. Individual slide layouts keep their Gallery or authored custom names. The master label is separate from the theme name: authored theme identities stay exact so import can recover them. Archive tests verify the emitted names; PowerPoint menu display is a separate native observation.

Current API (PPTX 0.20.0; core ^0.20.1 and optional renderer peer ^0.20.0): toPptx(presentation, { fonts }) takes the fonts handle returned by @openpresentation/opf-render/fonts-node's loadFonts() (core's Fonts, with at least textMeasurement). fromPptx(bytes, { signals: true }) returns { presentation, signals }. OPFPptxError carries findings (core format or variable errors) and diagnostics (layout-overflow diagnostics). Typeface helpers are inventoryTypefaces and checkTypefaces; the latter takes { families }. These API names replaced the earlier top-level textMeasurement, { document, signals }, issues, inventoryPptxTypefaces, checkPptxTypefaces and { fonts } check options in the historical RR-55 migration. Each slide is composed with core's resolveSlideContext options and its resolved design fonts. Unresolved references report unresolved-reference and use engine defaults unless strictReferences: true refuses the export. A slide without a layout, or with layout: "auto", composes automatically.

Version 0.12.0 requires @openpresentation/opf ^0.12.0 (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored) and exports what that core composes, plus the export and import changes listed in the changelog; use it with opf-render 0.12.0 (optional peer ^0.12.0) and opf-editor 0.11.0 so preview and export resolve one core. Version 0.11.9 exports an SVG image as a native SVG picture over a PNG fallback (rasterized in Node by the optional @openpresentation/opf-render peer or options.svgRasterizer; without one it stays the placeholder), writes a deck footer's first text, date and slide number as native PowerPoint Header & Footer placeholders (output change for decks with a footer; every export now carries the footer placeholders on its master, layout and notes master; see native header and footer) and adds the opt-in fromPptx(bytes, {signals: true}), which also returns the raw per-shape signals of every slide (see Import signals; the option leaves the default import unchanged; core floor ^0.11.4 unchanged, no API removed). Version 0.11.8 re-imports rich text that wraps over several native lines as the one authored payload instead of reporting content-structure-changed (the export records the line count; decks exported by earlier versions import as before; core floor ^0.11.4 unchanged, no API change). Version 0.11.7 requires @openpresentation/opf ^0.11.4 and exports the design fields natively (cover and section logos, header and footer logos, picture bullets, the accent font), writes every chart's text at the size the preview draws, round-trips the slide content structure, sections, extensions, assets and brand images through PPTX provenance (a fresh export of a plain deck re-imports in its authored form, so read a root payload field before blocks[0]), writes slide sections as PowerPoint's section list, and degrades an SVG image to the preview's placeholder instead of aborting; install it with renderer 0.11.9 and editor 0.10.6 so export and preview resolve the same core (no API removed). Version 0.11.6 exports the treemap, histogram, pareto, box-and-whisker, waterfall and funnel charts as native chartex parts by default (toPptx({chartex: 'auto'}); world stays a clustered column with chart-data-adapted; pass chartex: 'fallback' for the previous output) and gives chartex text the deck's label colour and font (no API removed). Version 0.11.5 writes the slide tag run as a theme color reference (a:schemeClr accent1) where the deck theme holds the primary there (no API change; the color is unchanged). Version 0.11.4 re-imports quote payloads (each native quote line carries an OPF_QUOTE_V1 tag) and slide-level image payloads from an unchanged export, writes the theme's a:ea and a:cs only for slots a script font is selected for, and requires core ^0.11.3 (the 70 legacy gallery layout ids and their geometry; install it with renderer 0.11.6 and editor 0.10.4 so export and preview resolve the same core; no API removed). Version 0.11.3 adds the opt-in toPptx({ chartex: 'native' }) export of the seven chartex chart types and always imports chartex charts (no API removed; the default output is unchanged). Version 0.11.2 exports the classic chart types as native chart constructs and writes named and default table colours, text pairs and hyperlink colours as theme references (no API or dependency change; resolved colours are unchanged). Version 0.11.1 exports design.watermark as a native picture (it re-imports) and embeds byte-identical media parts once, with no API or dependency change. Version 0.11.0 requires core ^0.11.2 (cover slides are centered between header and footer furniture) and the optional @openpresentation/opf-render peer ^0.11.0; install them together so export and preview resolve the same core. Version 0.10.0 required core ^0.11.1 and the peer ^0.10.0. It adds native slide-number and date fields, socials, slide-level image treatments, theme color schemes, script-slot fonts and RTL, re-import of design and metadata references, native underline and current-body formatting on import, and UTC-canonical zipDate (see the changelog for the intentional contract changes).

Chart options (RR-35, unreleased): axisTitles, legend and dataLabels on a chart export as native c:title, c:legendPos and c:dLbls (and cx:title, cx:legend, cx:dataLabels on the Office 2016 constructs) and import back; the preview draws the same options. See core docs/chart-options.md for the per-type support table and the chart-option-adapted diagnostic.

Chart emphasis (FA-14, unreleased): chart.highlight exports as native series fills (c:ser/c:spPr, plus c:marker/c:spPr on line, scatter and radar series) and per-point fills (c:dPt: c:spPr on a column or bar, a circular c:marker on a line, the slice fill on a pie or doughnut). The accent is a:schemeClr accent1 when the deck theme holds the primary exactly (a theme edit in PowerPoint recolors it) and an sRGB literal when the contrast adjustment moved it; the muted color is an sRGB literal (a mix of two theme colors is not a scheme color). Labels inside a mark take the color that contrasts with that mark. The highlight is not inferred on import: in full provenance mode it is recorded in the chart's OPF_DATA_V1 record and restored while the chart's cached data is unchanged. Chartex constructs are not highlighted. See core docs/chart-options.md.

Chart and table data (RR-54, unreleased; core docs/chart-table-data.md): chart values pass core chartNumber (a string that is not a plain decimal, such as "12%", "$5" or "(5)", is a gap and one chart-value-not-numeric diagnostic per chart, where the exporter used to strip it to a number). A column number format ({"name": "Revenue", "format": "$#,##0.0"}) is written as the series format code, the data-label and value-axis number formats (the axis takes the first plotted series' format, as the preview does) and the embedded workbook cell formats (Edit Data shows it), on classic and chartex charts. Top-level datasets referenced by {"dataset": "<id>", "fields"?} and chart.mapping export exactly like their inline equivalent; dataset tables and formatted table cells export their display text. With full provenance, OPF_DATASETS_V1 (beside the document tag) and OPF_DATA_V1 (on each chart frame that uses these fields and every table frame) restore the datasets, references, mapping, formats and data.source on import while the native caches (a plain PowerPoint save keeps them), the referenced datasets and the native table evidence are unchanged (tags are limited to 16 MiB and shape-checked; a bad one is reported as invalid-data-provenance); a deck without them maps every series format code that core fromExcelNumberFormat maps back to the column format and reports any other code (chart-number-format-adapted). Full provenance also records inline tables as authored, including scalar numbers and absent styling. Unchanged native cells restore that representation; cell text, formatting, table geometry or theme/style edits retain the observed native cells and report table-data-provenance-changed. references-only and no-provenance tables import displayed text and observed formatting, so scalar types and authored style absence are lossy.

Version 0.9.1 kept core ^0.11.0 and raised the optional @openpresentation/opf-render peer to ^0.9.0 so it coexists with editor 0.8.0. ColorRef / variables on content colors still hex-resolve through core resolveColorRef() before PptxGenJS srgbClr export. Unrecognized run colors such as color:'invalid' still validate and fall back to the theme text color. Native DrawingML schemeClr and theme clrScheme writes, and native p:hf headers/footers, are not in this release. Import still flattens theme colors to hex. Metric, quote and timeline layout placeholders and the corrected text-bullet contract from 0.8.1 are retained.

Unfinished prepared shaping work is preserved in the September 15 roadmap; it is not part of the published runtime.

This checkout supports Node 22 or later (engines.node >=22; CI tests Node 22, 24 and 26). Versions 0.8.0 to 0.12.3 declared 24.x, so npm on Node 22 or 26 silently installed an older release instead (RR-20); upgrade past 0.12.3. Use .nvmrc (Node 24) for local development. Earlier published versions retain their original engine declarations. Browser entrypoints remain browser-safe; native application compatibility is verified separately.

Version 0.8.0 consumes shared heading/scalar/rich line placement, including textRasterPadding, through editable native line boxes without autofit. Complete heading tags retain title/subtitle/tag roles, line ordering and current edited text during import. Source boundary metadata also records hard-line separators, so complete scalar/heading groups recover current native text with spaces, tabs and exact authored line endings. Incomplete, duplicate, ambiguous or bulleted groups fall back to ordinary import with diagnostics. Legacy heading tags retain their prior native-line behavior; arbitrary formatting, nesting and geometry are not reconstructed. See the source contract and limits. Native raster certification remains separate.

Pure local PowerPoint conversion tooling for Open Presentation Format documents. This repo owns the Phase 3 and Phase 4 toolkit lanes: OPF to PPTX export and PPTX to OPF import.

Version 0.7.0's shared-code integration preserves exact source/metadata through guarded native tags and editable lines. XML-forbidden code characters reject export with invalid-code-text, the OPF field path and UTF-16 offset; schema validation alone does not establish XML representability. Native source recovery does not reconstruct formatting, geometry or font theme.

Version 0.8.0 includes shared metric integration. Native alignment and exact tested field recovery are implemented; tab-position and raster fidelity gates remain open.

The native text verifier has completed 24 separately bounded cases on each supported Node runtime against its recorded source baseline. All 72 imports and 96 original-text ink masks per runtime pass; parent-owned temporary fonts are removed after success, failure and timeout controls. Those historical results do not cover subsequent font-selection changes, edited-text reflow, arbitrary native fidelity or the unresolved chart and metric gates.

Version 0.8.0 physical-font selection consumes optional fontFace metadata on accepted text styles. The provider supplies the physical family and its boolean bold/italic style-link flags independently of logical numeric weight. This lets a resolved Roboto SemiBold or ExtraBold face keep its regular legacy-family style rather than requesting a second bold style. Providers without metadata retain the previous numeric-weight behavior; malformed metadata fails with invalid-font-selection. Use core 0.10.0 and renderer 0.8.0. Verification and limits distinguish native selection flags from actual Office paint.

Version 0.8.0 also measures design.contentBox cards through core's padded interior and exports editable rounded frames at their outer allocation. Native tags identify unchanged empty generated frames on reimport, with a content-card-reflow diagnostic: frame appearance and placement are not reconstructed. A name alone never hides a shape; edited, untagged or ambiguous frames use ordinary native import, retaining text or an unsupported-shape description. These source checks do not establish native PowerPoint raster fidelity.

Scope

Version 0.8.0 requires core 0.10.0 and uses renderer 0.8.0 for coordinated preview/font measurement. Quotes and code export their accepted internal lines and styles without another fitting pass. Controlled Windows PowerPoint quote evidence records glyph containment, separation, save/reopen and text reimport against its exact source/font hashes. Native quote import restores the quote payload from an unchanged export (see Quote provenance); it does not restore the original typography or readability policy, and an edited or damaged quote imports as editable text blocks. Native chart geometry and general scalar-text wrapping also remain different from preview; editability and valid reimport do not establish raster equivalence.

  • Package: @openpresentation/opf-pptx
  • Repository: OpenPresentation/opf (packages/pptx)
  • License: MIT
  • Compatibility target: @openpresentation/opf
  • Renderer relationship: may use @openpresentation/opf-render for chart rasterization, the PNG fallback of SVG pictures and visual verification
  • Public export API: toPptx(opf, opts)
  • Public import API: fromPptx(buffer, opts) (opts.signals adds raw per-shape signals; see Import signals)

The export path validates OPF with @openpresentation/opf, maps slide titles and common content payloads to editable PowerPoint objects through pptxgenjs, then normalizes the generated ZIP for stable entry ordering, fixed timestamps, and reproducible bytes.

import { toPptx } from "@openpresentation/opf-pptx";

const bytes = await toPptx({
  $schema: "https://openpresentation.org/schema/opf/v1",
  name: "Quarterly Review",
  slides: [
    {
      title: "Revenue grew across all regions",
      items: ["North America +18%", "EMEA +14%", "APAC +11%"]
    }
  ]
});

await fs.promises.writeFile("quarterly-review.pptx", bytes);

toPptx returns a Uint8Array containing a PowerPoint-openable .pptx. It does not fetch remote assets. Data URI images and local paths can be embedded directly; hosts that need private asset loading should pass imageResolver(src, context). Every picture path (image blocks and region images, quote photos, slide and deck background pictures, watermarks, cover logos, header and footer images and logos, picture bullets) reports an unresolved source with exactly one unresolved-asset diagnostic at its OPF path and draws the preview's fallback (the placeholder, the background colour, no watermark, or the character bullets), so strict CLI export (--fail-on warning) gates it. A local file path that cannot be read is unresolved like any other source (reason: 'file-unreadable'), instead of failing the export. An imageResolver that throws (the CLI's does for a local image it cannot read) is unresolved the same way (reason: 'resolver-failed'): the export continues without that image, with its fallback and one unresolved-asset warning that carries the resolver's message and says the image was left out (a background keeps the slide's resolved background color, with no picture and no relationship written). strictAssets: true turns that failure into a missing-asset OPFPptxError (details.reason: 'resolver-failed'), like every other unresolved source; the CLI's --fail-on warning gates the warning. A cancelled export (AbortError) and an OPFPptxError the resolver throws still propagate. Set strictAssets: true to turn unresolved or remote image assets into structured OPFPptxError failures instead of editable placeholder boxes. The "Image unavailable" placeholder (a dashed panel OPF image placeholder N with its label lines, or a cross when no label fits) is one native group OPF image placeholder N group (opf-pptx#221) that carries the accessible name (Image unavailable: <alt>) as its alt text, so PowerPoint moves or deletes it as one object while each member stays an editable shape; a logo and a header or footer image keep their ungrouped panel (generated furniture is never grouped). In full provenance the panel of an image block carries an OPF_IMAGE_PLACEHOLDER_V1 tag (the slide, the block path, the authored image value and the block's own fields), and fromPptx turns it back into that image block; without the tag (references-only, provenance: false) or with a damaged one (invalid-image-provenance) the shapes import as ordinary content.

Embedded picture bytes are a PNG, JPEG, GIF, WebP or SVG. Bytes that are no readable image (or an SVG that cannot be exported: malformed XML, no xmlns, no width and height and no viewBox, or no rasterizer, see below) export what the preview shows, like an unresolved asset: the "Image unavailable" placeholder (nothing for a watermark, the background colour for a background image) and one unresolved-asset diagnostic with a reason (unsupported-format, svg-malformed, svg-no-size, svg-too-large, svg-unsafe, svg-unreadable, svg-rasterizer-unavailable, svg-render-failed); strictAssets throws unsupported-image-dimensions for an unreadable raster, invalid-svg-image for an unusable SVG, svg-rasterizer-unavailable or svg-render-failed.

SVG pictures are native SVG pictures, as PowerPoint 2016 and Microsoft 365 store them: a p:pic whose a:blip embeds a PNG fallback raster and carries the asvg:svgBlip extension that points at the SVG media part (image/svg+xml, content type registered). PowerPoint 2016+ draws the SVG (crisp at any zoom; Graphics Format, Convert to Shape); older viewers draw the PNG. This covers image blocks (placed or not), the alt-text picture of an image background, the watermark, header and footer images and logos, and cover and section logos. The picture takes the same frame and fit (cover, contain, stretch) as a raster of the same proportions; the SVG's size is its width and height (any unit), else its viewBox. Exceptions, each a raster only: a picture bullet (a:buBlip can reference a raster only, so it is the PNG), an image background without alt text (a background picture fill carries no SVG), a tiled background picture, and an image block with a duotone recolor or a non-rectangular shape (the effect is written on the PNG blip, so it applies as in the preview, and export-lossy with feature: "svg-rasterized" is reported). Opacity (a translucent watermark, slide image opacity), grayscale and the slide image border stay native SVG pictures: PowerPoint applies them to an SVG picture.

The PNG fallback is drawn at 192 dpi of the displayed size (a multiple of 128 px on its long side, 128 to 2304 px, transparent background) by options.svgRasterizer(svg, {width, height, scale, text}). In Node the default is opf-render's toPng (resvg with the bundled fonts and no system fonts, so the same SVG gives the same bytes on every machine), loaded only when an SVG is exported: it is the optional peer @openpresentation/opf-render. Without opf-render and without svgRasterizer (a browser build has no default; pass one that draws the SVG deterministically), the SVG picture is the placeholder with reason: "svg-rasterizer-unavailable". From opf-render 0.16 the PNG converter is itself an optional peer of opf-render: install @resvg/resvg-js next to it (npm install @resvg/resvg-js@^2.6.2; with text, also the font packages of the base pack, @expo-google-fonts/roboto and roboto-mono). When opf-render rejects with converter-missing the exporter reports the same svg-rasterizer-unavailable and the message carries opf-render's install command; with strictAssets the export throws that code. An imageResolver that returns a raster for an SVG source still gives a plain raster picture, and one that returns SVG bytes (mediaType: "image/svg+xml") gives a native SVG picture. A local .svg path is read in Node.

An SVG is never run or fetched. It is validated as XML and sanitized before it is embedded (and on import): <script>, <foreignObject>, event-handler attributes, link animations, every href/url() that is not a #fragment or an inline PNG, JPEG, GIF or WebP, @import rules, xml-stylesheet instructions and DOCTYPE declarations are removed (a DOCTYPE entity that is plain text is expanded; an external or nested entity is refused as svg-unsafe), and svg-sanitized reports what was removed. An SVG that needs none of that is embedded byte for byte, so import returns the authored source.

With diagrams: "picture" (and for every diagram that cannot be written as shapes; see SH-07 below), diagrams (diagram, Mermaid source) export through the same SVG picture path: each is one native SVG picture OPF diagram N (an asvg:svgBlip over its PNG fallback, the fallback drawn by options.svgRasterizer or opf-render's toPng) at the box core drew it in (item.diagram.box, contained and centered in the item box, with no crop or fit of its own). The SVG is core's paintDiagram of the composed drawing, sized to that box and self-contained: every CSS variable and color-mix resolved to a literal color, styles moved to presentation attributes, no classes, the labels kept as SVG text in the slide's body family (the code family for code-like labels), its <title> the alt text. Its palette is core's diagramPalette with the inputs opf-render's preview uses (slideDiagramPalette): the surface the diagram stands on (its card's fill, else the slide background or the emphasis surface), the slide's text and muted text, the deck color scheme with the variant's accent order, the scheme's dark1 and light1, and the families the slide was composed in, so the picture paints exactly the colors of the preview on a light or a dark slide. The picture's description (descr) is the diagram's alt, or the alt core derives from its labels. A caption follows the picture as for a chart or an image. A diagram is a picture, not native editable shapes: PowerPoint can resize, crop or recolor it, and Graphics Format > Convert to Shape turns the SVG into ungrouped shapes, but its nodes and connectors are not PowerPoint objects with text you edit in place; edit the source and export again. Because the PPTX draws what the preview draws, no export-lossy is reported for it. A source that cannot be drawn exports the preview's unavailable-diagram placeholder (one dashed, unfilled shape OPF diagram placeholder N in the border neutral that reads "Diagram unavailable") with content-placeholder (reason: "diagram-unavailable") beside core's unresolved-content; a diagram whose fallback cannot be rasterized exports the same placeholder with unresolved-asset (reason: "svg-rasterizer-unavailable" or "svg-render-failed"), and strictAssets throws instead, as for any SVG picture. Diagrams never bind to a layout placeholder.

In full provenance the picture (or the placeholder) carries an OPF_DIAGRAM_V1 tag: the slide, the block path, the authored diagram value (source, syntax, alt as written), the alt text written as the description and, for a picture, its exact native geometry, blip fill and the media key of its SVG part. fromPptx turns an unchanged tagged picture back into the diagram (a root diagram or a {type: "diagram"} block, with its caption), and an intact placeholder too; the picture's current description is the alt text, so a description edited in PowerPoint becomes alt. A picture that was moved, resized, cropped, recolored or replaced (Change Picture), renamed or tagged twice imports as an ordinary SVG image and reports invalid-diagram-provenance. With provenance: "references-only" or false no diagram source is stored and the picture imports as an SVG image.

SH-07: by default (toPptx(deck, {diagrams: "shapes"}); "picture" is the path above) the export writes a diagram of a family in core's DIAGRAM_SHAPE_FAMILIES as one p:grpSp OPF diagram N of native shapes built by src/diagram-shapes.js from the same painted SVG (with pele's classes kept): presets where pele's outline is exactly one (rect, roundRect, ellipse, diamond, hexagon, parallelogram, trapezoid, triangle, checked against SH-01's geometry), custom geometry otherwise, transforms flattened, fills and lines with alpha, gradients, dashes, caps and joins, labels as real text at pele's baselines (exact line spacing, first baseline at three quarters of the pitch, as PowerPoint places it), a node's label as the node's own text, a straight edge without arrowheads as a straightConnector1 glued (a:stCxn, a:endCxn) to the node sites it touches, and every other edge as one custom shape of its exact line and arrowheads (PowerPoint refuses custom geometry in p:cxnSp). The group's description is the alt text; in full provenance its OPF_DIAGRAM_V1 manifest has shapes: true and the group's signature, so an unchanged group imports as the diagram and an edited one as its shapes with invalid-diagram-provenance. A family not in the list, or a drawing with a clip path, mask, filter, pattern, image, text path or right-to-left text, stays the picture with diagram-shapes-unavailable (info). See core docs/diagrams.md#native-shapes-in-powerpoint.

fromPptx reads a picture that carries asvg:svgBlip back as the SVG (a data:image/svg+xml;base64, source), not its PNG fallback, from this exporter's files and from PowerPoint's (a damaged or hostile SVG part imports its PNG fallback and reports invalid-svg-image, or imports sanitized with svg-sanitized). Design fields, organization logos and blocks with SVG sources (organization.logo, design.watermark, image blocks with their treatments) round-trip as SVG.

fromPptx parses an existing .pptx buffer locally and returns an OPF document that validates with @openpresentation/opf:

import { fromPptx, toPptx } from "@openpresentation/opf-pptx";

const opf = await fromPptx(await fs.promises.readFile("source.pptx"));
const roundTripBytes = await toPptx(opf);

await fs.promises.writeFile("round-trip.pptx", roundTripBytes);

The importer reads core properties, slide order, text boxes, speaker notes, embedded images, tables, and basic cached chart data from the OOXML parts. Slides or objects that do not map cleanly fall back to editable blocks[] payloads; OOXML positions are used for deterministic ordering and title/subtitle detection while keeping the emitted OPF schema-valid.

New in 0.10.0: native notes and property whitespace

Current source preserves spaces, tabs, NBSP and authored CR/LF/CRLF in speaker notes and scalar presentation name, description and author; published npm 0.9.1 does not contain this repair (0.10.0 does). Import reads current native notes body paragraphs in run/field/line-break order, retaining blank paragraphs. Explicit paragraph and line-break boundaries import as LF. XML character references decode once, so literal text such as &#13; remains literal. Export writes authored CR as character references in native text, without adding source-recovery tags; ordinary exports with no authored CR remain byte-identical.

Native edits, cleared/deleted note bodies or parts, core-property edits/deletions and slide relationship order remain authoritative in all provenance modes. Empty notes and absent notes still both import absent; an empty or absent title uses the existing fallback, and empty description/author import absent. Author arrays still export as a joined scalar. External literal XML line endings follow XML normalization; exact CR requires character references. These are portable XML conversion controls, not native Office or visual acceptance.

v1 Placeholder and OOXML Mapping

The first exporter keeps the public API stable while using pptxgenjs internally:

  • Slide.title, Slide.subtitle, and Slide.tag become editable text boxes, not PowerPoint master placeholders.
  • Root payloads, blocks[], and promoted region keys become editable slide objects in deterministic regions. Promoted keys use the OPF 3x3 region vocabulary (top, middle, bottom, left, center, right).
  • Text, lists, metrics, quotes, timelines, code, tables, and inline-data charts are emitted as editable PowerPoint text, table, and chart objects. Content ColorRef values (hex, scheme slots/roles, and var:<id>) resolve through core resolveColorRef(). Slot and role names become native a:schemeClr references when the exported theme holds that exact color; hex, var:<id> and slide-override colors stay a:srgbClr (see Theme color scheme). Each classic chart type the core catalog keeps (one per native construct: clustered/stacked/100% stacked column and bar, line and stacked line with or without markers, area/stacked/100% area, pie, doughnut, scatter with markers, radar/radar with markers/filled radar) is written as its exact native construct; deprecated core ids export as their replacement; other ids keep the previous best-effort mapping. The chartex types treemap, histogram, pareto, box & whisker, waterfall and funnel are written as native Office 2016 chartex parts (confirmed in desktop PowerPoint on 2026-09-30; see Chartex charts); the map (world) stays a clustered column chart by default and reports chart-data-adapted (chartex-fallback) until PowerPoint accepts its regionMap part (chartex: "native" writes it anyway, chartex: "fallback" writes clustered columns for every chartex id). A pie or doughnut chart, or a one-series chartex construct, plots only its first series and reports chart-data-adapted (series-dropped) when it has more. Every classic chart part follows the dml-chart.xsd child sequences (opf-pptx#175): each series is written in its CT_*Ser order (a line series has c:marker before c:dLbls), a radar series carries no c:invertIfNegative, and the scatter X c:valAx carries no category-axis elements.
  • A timeline event's status (done, current, planned) is drawn from the deck's colors with core's shared shapes (the same as the preview): a filled ellipse marker for done and for no status, a current marker inside a ring (a second ellipse, tagged with its marker) with a bold label, and a planned marker that is an ellipse with the background fill and an accent outline, with muted text kept at 4.5:1 or more. Import restores status from the marker tags, not from the shapes' look, and drops the timeline provenance (keeping plain text) when a current event's ring is missing or duplicated.
  • A chart whose data is one column of values (a histogram or dot plot) has no category column. A histogram or Pareto chart bins the values itself (PowerPoint's histogram with an explicit automatic bin count, no diagnostic) and re-imports as authored; any other chart type plots the values against their row numbers and reports chart-data-adapted (row-numbers). Full provenance restores the authored data form while the native caches are unchanged; edited caches, references-only and no-provenance imports retain the observed row-number categories and values. With chartex: "fallback" a histogram is binned into equal-width bins (Sturges' count, at most 50) and written as a column chart of the counts (histogram-binned; the binned counts do not round-trip). Cells parse as in every chart (core chartNumber: numbers and plain decimal strings; "12%", "$5" and "1,234" are not numbers) and cells that hold no number are skipped, not plotted as 0. Chart data that cannot be plotted (no numbers, no rows, or an external data source), an empty table and unsupported content keep a placeholder frame with a plain-language description (never a dump of the source data or URLs) and report chart-data-unplottable or content-placeholder with a reason; content is never dropped without a diagnostic.
  • Image assets are embedded only when supplied as data URIs, local paths, or host-resolved bytes/paths. Remote asset URLs are never fetched by the runtime path. A picture's native description is the authored alt (never a stand-in such as preencoded.png or the file path), and a distinct asset title is the picture's native title; fromPptx reads both back.
  • A chart cell that holds no number (null, an empty or non-numeric string, a boolean) is a gap in the native chart: its cache has no point at that row (c:ptCount keeps the row count), its workbook cell is blank, and it re-imports as null. An actual zero stays zero.
  • ZIP entries, generated chart/workbook part names, core-property timestamps, and nested chart workbook timestamps are normalized for reproducible bytes. Export determinism lists the tested time zone, locale, clock and host-font controls and each known variance (WebP conversion, font registries, ICU segmentation, timestamps).
  • The package names only the document's chosen fonts. Chart text (data labels, axes, legend, titles) uses the chart slide's body font in latin/ea/cs; each embedded chart workbook uses the same fonts in its styles and theme; run pitchFamily follows the font scheme type (monospace is fixed pitch, serif is roman); and docProps/app.xml "Fonts Used" lists the fonts the package actually uses. The theme keeps PptxGenJS's per-script supplements (THEME_SCRIPT_SUPPLEMENTS) and empty ea/cs slots unless a script font was selected for that slot (see Languages, right-to-left text and script fonts).
  • checkTypefaces(bytes, {families, monospace, themeScripts, themeScriptsByPart}) (optional themeScripts: {major: {ea, cs}, minor: {ea, cs}} names the East Asian / complex-script families the author selected; a selected theme slot that is empty or different is reported as theme-script-slot, an unselected one may stay empty; themeScriptsByPart overrides it per theme part, for the theme of a per-slide script master; theme references resolve against the theme of the part's own slide or notes master) inventories every typeface, workbook font name and "Fonts Used" entry in every XML part, including nested packages, and reports each font outside that policy. inventoryTypefaces() returns the raw inventory.

This pass did not require an OPF schema change. The deferred full OOXML placeholder mapping from docs/plans/layout-placeholders.md remains a later hand-written OOXML emitter concern.

New in 0.10.0: explicit ZIP dates

The following behavior is in current source; the published npm 0.9.1 package does not contain this repair or tightened option contract; 0.10.0 does.

Omitting zipDate (or passing undefined) keeps the established fixed 1980 ZIP bytes. Explicit zipDate values now encode UTC calendar fields in both the PPTX and embedded workbook ZIPs, independently of the host timezone. This option changes ZIP metadata; the separate timestamp option controls core-property XML.

Accepted values are a valid Date, finite epoch milliseconds, YYYY-MM-DD (UTC midnight), or YYYY-MM-DDTHH:mm[:ss[.fraction]] ending in Z or ±HH:mm. Calendar components must be valid and the resulting UTC year must be 1980–2099, matching the existing ZIP writer's supported range. ZIP timestamps have two-second resolution: fractional and odd seconds are truncated.

This intentionally tightens the previous host-dependent Date parsing contract. Datetimes without a timezone, legacy date strings, invalid dates, and values outside that range throw OPFPptxError with code invalid-zip-date and path options.zipDate. Explicit null, '' and 0 no longer silently use the default (0 is a 1970 epoch date). Existing successful UTC output and default output remain byte-identical; explicit dates on other timezones change to the canonical UTC result.

v1 Import Mapping

Quote provenance (FF-57)

A quote payload exports as native text lines: the body (the quoted text wrapped in straight quotation marks) and, when there is an attribution, role or source, one footer: the attribution, the role on its own line below it, and the source after - on the last line. Each line shape carries an OPF_QUOTE_V1 tag that stores only topology (whether the value was the string shorthand, which footer fields exist, where each separator falls, whether the quote has a photo, and how many source lines each part has). No quote word is stored: every value is read back from the current native text, so a cleared or edited quote cannot bring back the words it once held.

An unchanged export re-imports as { "type": "quote", "quote": ... } exactly: the string shorthand stays a string, text/attribution/source come back with their whitespace, hard line breaks and inner quotation marks, and several quotes on one slide keep their order. Native edits to the body or footer text import as the edited quote. The importer reports quote-import-reflow (native formatting, position and font theme are not reconstructed) and quote-footer-merged when an edited footer no longer separates attribution from source. A missing, duplicated or reordered line, a changed manifest or a native bullet on a tagged line rejects the group: the shapes import as ordinary text blocks with an invalid-quote-provenance diagnostic and no old words are restored. A quote authored without tags (for example in PowerPoint) imports as text. The output stays ordinary editable PowerPoint text boxes; the tags are p:custDataLst entries that PowerPoint keeps.

A quote with a photo (FA-12) also exports the headshot as one native picture in the circle frame core computes beside the footer lines: cropped to fill it (a:srcRect), with the ellipse preset geometry, the asset's alt text as the picture description, and the same OPF_QUOTE_V1 group tag (role photo, no words). Import restores role from the footer and photo (src, alt) from the current picture; a picture that was deleted imports the quote without a photo and reports quote-photo-missing, and a renamed or duplicated one rejects the group like a damaged text line. An SVG photo exports as its PNG raster (export-lossy, svg-rasterized), because the circular mask is not confirmed on an SVG picture in PowerPoint. An unresolved photo source draws the ordinary image placeholder.

New in 0.10.0: current native body formatting

Current source imports supported formatting from ordinary untagged native body text and list items. A value that previously imported as a string can now be a rich-run array containing the same current characters with explicit native properties. Unstyled values remain strings; title/subtitle selection and tagged recovery stay separate. Run, field and break order, blank paragraphs, significant whitespace, explicit normal overrides, paragraph/list defaults, point sizes, Latin font families, supported colors/alpha, hyperlinks and script direction come from the current PPTX. Edits, clears and deletion remain authoritative in every provenance mode. Native weight faces of the bundled Roboto family (for example Roboto SemiBold) import as Roboto plus bold for weights of 600 and above. Medium (500) and lighter weights import as regular, because OPF runs have no numeric weight, so a round trip re-exports Medium as Regular; approximate-body-font-weight reports this. Authored families such as Arial Black or Aptos Light keep their names.

This does not reconstruct original source run identities or boundaries between native shapes, infer master/layout text styles, or recover cached authored content. Native paragraph/break boundaries become LF; character-reference CR remains explicit. Unsupported properties report body-path diagnostics rather than claim exact formatting. Reading order still uses current native positions. Published npm 0.9.1 does not contain this representation change; portable conversion checks do not establish Office rendering or general rich-text round-trip fidelity.

The first importer is mechanical and schema-compatible:

  • Presentation core properties map to OPF name, description, and author.
  • The first slide master's theme clrScheme maps to design.colorScheme: the bare id of a colour scheme in the host default catalog (the first of fromPptx's catalogs) on an exact twelve-slot match, otherwise inline slots. A theme named like a theme of that catalog maps to design.theme when its colors or heading/body fonts corroborate it. With no catalog registered the colour scheme is inline and no theme id is recovered (see Theme color scheme).
  • Rich headline text (FA-10): a TextRun[] title, subtitle or tag exports as one tagged native text box per laid-out line with its runs (color, weight, italic, links, super/subscript, citation markers), and a TextRun[] quote.text the same way under the quote tags. Import returns TextRun[] where a heading's native runs are formatted differently from one another and a plain string where they are uniform: a tagged export records the heading's own weight, color, size and family (base), so only authored run formatting comes back, and a native title placeholder is read like body text, dropping what every run shares. A weight that the export writes as bold on every run of a title (and on a quote body) is the default, so only a bold: false word in a title is kept; bold: true in a quote body is indistinguishable from the body weight and is not restored. Marker runs become cite/footnote again, and heading and quote runs take part in the run-color reference slots.
  • Native title/subtitle placeholders retain their roles. On slides without complete OPF heading tags, recognizable text-box positions and sizes provide a fallback. If any complete OPF heading role is recovered, untagged body text stays in blocks[] instead of being promoted into an absent heading role. Damaged tags retain visible text through ordinary import and diagnostics.
  • Remaining text boxes map to blocks[] as text or list payloads, sorted by OOXML position. A measured list writes its size, family, weight and colour on every native run; in full provenance the slide record (OPF_SLIDE_V1.listRuns, opf-pptx#212) keeps that look per list (entry and description lines, never text), and import removes a run value still equal to it, so an untouched list round trip returns its items as authored instead of baking those values into TextRuns. A value edited in PowerPoint differs and comes back as observed; a run colour reference that resolves to the list colour (the text role) still returns through the run colour record, but an authored literal colour, size or family equal to the list's own cannot be told from it and returns absent. Without the record (references-only, provenance: false, a plain PPTX) runs are read as observed.
  • PowerPoint tables map to OPF table blocks, embedded images map to data URI image blocks, and cached chart series map to OPF chart blocks whose type is the core chart type id for the native construct (for example a percentStacked column chart imports as 100pct-stacked-column, a filled radar as filled-radar; a scatter chart imports as [Point, X, ...series]).
  • Chart cache points are placed by their native c:pt@idx, with c:ptCount retaining trailing gaps. Missing labels and values become null; explicitly empty labels and series names stay empty strings, and an actual numeric zero stays zero. Empty numeric cache values remain missing. Malformed or duplicate indices, contradictory counts, competing caches and allocation limits reject with invalid-chart-cache and the chart-part/cache path; hierarchical category caches reject with unsupported-chart-cache instead of flattening labels. Each cache is limited to 100,000 positions, and combined caches and the emitted table each to 1,000,000 cells. Complete decimal scientific notation in numeric caches is read as a number; exponent overflow or nonzero underflow to zero rejects with unsupported-chart-cache and the chart part, series, cache and logical point path. Zero coefficients remain zero. This finite-number boundary does not classify rejected text as invalid OOXML. Malformed exponent strings and other nonblank numeric text retain the existing legacy conversion policy. This cache-only import does not repair embedded worksheets, restore unsupported scatter X/point labels, change exporter/workbook parsing or export/re-export missing-data handling, or establish native chart fidelity.
  • Explicit native solid line-series paint imports with a chart-option-adapted diagnostic at the native slide/chart/series locator (series.N.line.color). OPF has no per-series color field, so it does not retain that paint override or infer semantic highlight from it. Data, design and restored highlight remain unchanged. This reports unsupported formatting, including an explicit default, rather than establishing a visual mismatch; other series/point/marker paint remains outside this bounded diagnostic. The preservation gap is tracked in opf-pptx#272.
  • Table imports retain empty rows. A native firstRow flag of 1 or true maps the first row to column labels; absent/false flags retain every row as data. New exports set this flag from OPF columns. Older exports without the flag retain their labels as the first data row rather than inferring headers.
  • Native table text preserves run/field/break order, significant whitespace, and blank paragraphs. Cells return canonical {value, style} objects; value retains scalar text or supported rich runs. Covered merge positions are null. Explicit normal headers override OPF’s bold header default. Numeric/boolean/null source types cannot be reconstructed from native display text.
  • Run lang maps back to the presentation language as a BCP-47 tag (the run's own tag, for example ja-JP); see Languages, right-to-left text and script fonts.
  • Imported runs retain bold, italic, underline, strike, point sizes, Latin font families, solid colors/alpha, external hyperlink URLs, and superscript/subscript direction. List-level and paragraph defaults apply before run overrides; supported theme fonts/colors resolve from the archive. Field values become their cached text, and underline/strike variants and baseline magnitudes reduce to OPF booleans.
  • Citations, footnotes and captions (core RR-34 fields): a marker exports as a native superscript run (baseline="30000") after its run; the slide's footnote area is a line shape plus one tagged text box per listed line (OPF_FOOTNOTES_V1), a caption one tagged text box per line (OPF_CAPTION_V1) naming its media shape, both at core's geometry; references travel in the document record. Import re-attaches tagged captions to their media block, rebuilds references, cite and footnote from the footnote tags and the marker runs (removing the marker runs; an edited note keeps its edited text), and guesses nothing without tags: a superscript number stays a superscript run and a text box stays a text block.
  • Conditional table styles, merged-cell geometry, cell fills/borders/alignment, unsupported text fills/colors, and internal hyperlink actions are not fully reconstructed. onDiagnostic reports unsupported table style references, merges, fonts/colors/fills and links with native frame/cell paths. Table paths use the native graphic-frame and row indexes, including a header row. The shared core 0.6.0 layout sizes rows from their content and reports text-overflow when text cannot fit at the minimum size. These checks establish native XML conversion, not visual parity with PowerPoint.
  • Unknown non-text shapes and unsupported graphic frames become editable text fallback blocks instead of failing the import.
  • Catalog references (design.theme, colorScheme, fontScheme, dimensions, background, slide layout), slide ids and authoring metadata (narrative, tone, audience, purpose, language, organization, speaker, ...) are stored at export in OPF_DOCUMENT_V1 / OPF_SLIDE_V1 customer-data tags. Import restores a reference while the theme colors, theme fonts, slide size, background or slide arrangement it produced are unchanged. After an edit, the observed native values stay and design-reference-changed / layout-reference-changed name the reference. toPptx option provenance: 'references-only' | false limits or disables these invisible tags. In full mode the document tag also lists the document-level defaults the document left to the engine ($schema, name, language, the theme, colour scheme, font scheme and slide size), and import leaves them absent while the package still shows the default, so an untouched round trip is deep-equal to the source (RR-59); a value edited in PowerPoint imports as observed. Slide layout intent (layout reference, type, composition, composition hints and the embedded layout record with its catalogs group and source) lives in each OPF_SLIDE_V1 record, so a slide keeps its layout even without the document tag, for example when it is pasted into another deck (FF-29). A layout that resolves only in a registered catalog stores no record and restores where fromPptx is given that catalog. See document round trips for exactly what is embedded.
  • The slide's content structure is part of OPF_SLIDE_V1 too (full mode): nested group blocks, promoted regions (left, top:left, ...), a root payload (text, items, chart, ...) versus blocks, block ids and extensions, group composition, with the reference-pixel box of every leaf from the same composition the export drew. Import matches the native shapes to those boxes and rebuilds the authored form while the slide's arrangement is unchanged, so slide.type validates again; a slide whose blocks no longer fit the stored boxes keeps its flat blocks and reports content-structure-changed at slides.N; a duplicated slide reports duplicate-block-id and drops the repeated id. The document tag also stores filename, root extensions, slide section and extensions, design.logo (deck and slide: a logo reference or false), the organizations with their logos and the whole assets registry; a data: source that is not an exported picture is stored inline up to 256 KiB, so an organization logo or speaker photo given as a data URI round-trips.
  • Slide section labels are written as PowerPoint's native section list (p14:sectionLst in ppt/presentation.xml, whatever the provenance option): one section per run of consecutive slides with the same label, runs without a label as Default Section, with deterministic ids. Import reads the list back (Default Section means no section); it wins over the stored value (a footer's {{slide.section}} is only the token, so no footer text names the section), except that the stored label stands while the list still names it. The native PowerPoint check of the sections pane and save/reopen is a separate gate (scratchpad/spec-gaps-native/).

There is no AI classification pass in the OSS runtime. Hosts can run optional cleanup or semantic remapping after fromPptx returns. A host that wants to rebuild structure (code, metrics, quotes, timelines, regions) in a deck that did not come from OPF can ask for the raw shape facts such a step needs; see Import signals.

Import signals

A deck exported by this package carries OPF provenance tags, so its structure returns exactly. A deck from anywhere else does not: a code panel is a text box with a monospace font, a metric is a large number over a small label, a quote is italic text over an attribution, and fromPptx imports each as plain text, because telling them apart takes judgement. That judgement is not made here. fromPptx only reports what is in the package, so a host can run its own classifier (a model, a rules engine, a person) over it. The runtime policy is unchanged: no network, no models, no clock, no randomness, nothing fabricated.

import { fromPptx } from "@openpresentation/opf-pptx";

// Default: the presentation.
const presentation = await fromPptx(bytes);

// Opt in: the same presentation, and a signals value beside it.
const { presentation: same, signals } = await fromPptx(bytes, { signals: true });
// or with limits: fromPptx(bytes, { signals: { maxSlides: 100, maxShapesPerSlide: 150 } })

Without signals (or with false) the return value and the document are byte for byte what they were; with it the presentation is identical and the result is {presentation, signals}. Invalid limits throw OPFPptxError with code invalid-signals-option before any work. signals is plain JSON (JSON.stringify and back loses nothing), the same bytes in always give the same signals, and the types are PptxSignals in index.d.ts.

What it reports. signals.deck has the slide size (EMU and reference px, 96 per inch), the theme (colour and font scheme names, major and minor font, the twelve colour slots) and whether the deck carries OPF tags. signals.slides[i] has the part, layout (name, type, part), master (part, name, theme name), whether the slide is hidden, whether it carries an OPF slide tag, and shapes: every shape in z-order (document order, back to front; zOrder 0 is the back), each with:

  • kind: text, shape (an autoshape with no text), placeholder (an empty placeholder), picture, media (video or audio), table, chart, smartart, ole, group, connector, line or unknown (with graphicType). placeholder carries type, idx and size.
  • box: px (reference px) and emu, in slide coordinates with group transforms (offset and scale) applied, so a shape inside a scaled group has its real position. A placeholder with no box of its own gets the layout's, then the master's (box.source). rotation, flipH and flipV describe the shape; its box is the frame before rotation, and box.approximate marks a box inside a rotated or flipped group.
  • parent, children and depth give the group nesting; id is s plus the z-order index.
  • text: paragraphs with level, align, the effective bullet (inherited from the list style when the paragraph states none), indents and spacing, and runs with font, size, bold, italic, weight, underline, strike, baseline, caps, color (#RRGGBB) with its theme colorRef, monospace, link, field and lineBreak. Values are the effective ones PowerPoint draws: the run, its paragraph, the shape's list style, the layout and master placeholder, the master text styles, the presentation defaults and the theme (font tokens such as +mj-lt and scheme colours are resolved). A value no part of that chain states is omitted, and defaults are omitted (bold, italic, monospace absent means false, weight absent 400, level absent 0, align absent left). monospace is true for a known fixed-width family (Consolas, Courier New, Menlo, JetBrains Mono, ...); text.monospaceShare, dominantFont, dominantSize, maxFontSize, anchor and autofit summarise the shape.
  • fill, outline (kind, colour, width, dash, arrowheads) and geometry (rect, roundRect, ellipse, ...). Fills and lines that only name a theme style are reported as kind: "style" with the style index and colour.
  • picture (part, media type, bytes, natural size, crop), table (rows, columns, widths, header flags, cell text), chart (chart types, series and point counts, title).
  • opf: where the importer put the shape in the document it returned. {role: "block", path: "slides.2.blocks.1", blockType: "text"}, {role: "title", path: "slides.0.title"}, or null when the importer made nothing of it (a connector, a table inside a group). For an OPF export the path follows the rebuilt structure (slides.0.blocks.0.blocks.1, slides.1.left, slides.2.text). A shape the importer consumed without a block of its own has a role only (furniture, logo, watermark, background, image-overlay, or the member of a tagged group). A group links where all its members do. Several shapes can link to one block (the lines of a list, the parts of a quote).

Bounds. The output is bounded by signals limits, reported in signals.limits (defaults, with the largest value each accepts): maxSlides 300 (5000), maxShapesPerSlide 300 (5000), maxTotalShapes 5000 (100000), maxParagraphsPerShape 200 (5000), maxTextCharsPerShape 8000 (200000), maxTextCharsTotal 400000 (5000000), maxTableCells 400 (10000), maxTableCellChars 200 (5000), maxGroupDepth 12 (32). What a bound drops is counted: signals.truncated (slides not reported, text budget exhausted), slide.truncated (shapes, depth, paragraphs, text, table) and paragraph.truncated. text.chars and text.paragraphCount are the true counts. Images are never embedded, only described, so the size follows the text: about 0.7 KB of JSON per shape.

Not claims. Signals are facts, not conclusions: a monospace run is not a code block, and opf: null is not an error. They are read from the package, not rendered: the effective values follow the OOXML inheritance rules, not PowerPoint's layout engine (text wrapping and autofit are not computed, a group's rotation is not applied to its members' boxes, and an unresolvable colour or font is left out). They describe the upload, which can be hostile: the text is the deck's text, and a host that passes it on to a model should treat it as untrusted data, never as instructions.

Combo charts (FA-15)

type: "combo" is exported as one classic chart part: one c:plotArea with a clustered column c:barChart on the primary axes and a c:lineChart (markers, c:grouping standard) for the line series. Lines named in chart.secondaryAxis form a second c:lineChart on a second c:valAx (axPos r, crosses max, its first series' number format, no gridlines, axisTitles.secondary as its c:title) and a deleted second c:catAx. Core's resolveChartData orders the series (columns, then primary-axis lines, then secondary-axis lines), each keeps its palette colour by its place in the whole chart, the embedded workbook holds every series, and a gap breaks a line (c:dispBlanksAs gap) as in the preview. The line groups are rewritten into dml-chart.xsd order (c:grouping first, no c:invertIfNegative, c:marker before c:dLbls). Import reads a plot area with one clustered column group and one or two line groups as combo, with line (only when it is not the default last series), secondaryAxis, the secondary axis title and the data label position; the authored column order comes back through the chart's data record.

Chartex charts (FF-22b)

Modes. toPptx(document, {chartex}) takes "auto" (default), "native" or "fallback". The native check of 2026-09-30 (desktop PowerPoint, read-only, the program's FF-22b deck set) opened every chartex deck without a repair prompt and reported the native Chart.ChartType for treemap (117), histogram (118, by value and by category), pareto (122), box-and-whisker (121), waterfall (119) and funnel (123); for world PowerPoint took the mc:Fallback (51, clustered column) when the choice required cx5. The recheck of the same day showed that PowerPoint accepts the regionMap part when the choice requires cx4 (the 2016/5/10 region-map namespace; cx3, cx4 and cx6 reported ChartType 140, cx5 and cx8 fell back) but, with no cx:geoCache, shows "There was a problem getting the information for your map chart" and draws nothing until it can fetch map data online. Supervisor decision: "auto" writes the chartex parts for the six confirmed constructs and keeps world on the clustered column chart, which always renders, with chart-data-adapted (chartex-fallback); "native" writes every chartex part, the map included with Requires="cx4" and the chart-map-geodata diagnostic (provider data is never fabricated); "fallback" writes clustered columns only, byte for byte as before FF-22b (test/fixtures/chartex-fallback-main.json pins it, with chartex-fallback and histogram-binned). The same check showed chartex labels drawn in the chart style's theme grey and box lines in black on a dark theme, so every chartex text element and the style part now carry the deck's label colour and font explicitly, and box series carry a line in the label colour. Import of chartex parts is always on.

The seven kept chart types with no ECMA-376 construct are written as Office 2016 chartex parts (ppt/charts/chartExN.xml, cx:chartSpace, content type application/vnd.ms-office.chartex+xml), each with its chart style and colour style parts (styleN.xml, colorsN.xml) and the embedded workbook the data came from. PptxGenJS cannot write these parts, so the exporter first writes the classic clustered column chart of the same data and then post-processes the package (src/chartex.js): the slide's chart frame becomes an mc:AlternateContent whose mc:Choice (requiring the construct's chartex namespace) references the chartex part and whose mc:Fallback keeps the classic chart frame, so a reader without chartex support still shows the data as columns. Both parts share the workbook. `cx:series