Skip to content

feat(ppt-codec): write real MS-PPT presentations - #877

Merged
Mearman merged 3 commits into
mainfrom
worktree-agent-a27bf3e0aab3fe8cc
Sep 3, 2026
Merged

feat(ppt-codec): write real MS-PPT presentations#877
Mearman merged 3 commits into
mainfrom
worktree-agent-a27bf3e0aab3fe8cc

Conversation

@Mearman

@Mearman Mearman commented Sep 3, 2026

Copy link
Copy Markdown
Member

Summary

Adds a genuine [MS-PPT] write path to ppt-codec, alongside the existing reader. This is not full read/write parity with #817's stated scope, and does not close it — see below for exactly what's covered.

  • One slide per input slide, each a plain text box (no images/tables/masters/layouts/scheme colours/notes/metadata).
  • Paragraph text with per-run character formatting: bold/italic/underline, font family (resolved through a real document-wide FontCollectionContainer), size in points, and literal RGB colour.
  • Paragraph alignment and list/indent level.
  • A real single-edit persist layer (CurrentUserAtom -> UserEditAtom -> PersistDirectoryAtom) and a genuine [MS-CFB] compound file via archive-codec's writeCompoundFile.
  • Content this writer cannot express throws PptUnsupportedContentError (a non-presentation document, or slides that don't share one size, since DocumentAtom states exactly one slide size for the whole presentation). A block kind the shape tree has no room for (image/table/embeddedObject/construct marker) is silently excluded from the written text body, mirroring how the reader already documents its own unsupported constructs rather than erroring on them.

Every record's byte layout is the direct inverse of the equivalent read function's own field order (not a fresh guess at the spec) — record/write.ts and each *-write.ts module cites the same [MS-PPT]/[MS-ODRAW] sections the corresponding reader module already does.

Verification

The primary verification is a direct round trip through this package's own, independently-built reader: write real records, then read them back via readPptContent/readPpt, and assert the recovered content equals what was written (write.test.ts, content-write.test.ts, text/style-write.test.ts — 38 new tests). This proves the written bytes are genuinely conformant [MS-PPT], not merely internally self-consistent, since the reader was built and tested against the specification alone before any writer existed. The Cloudflare Workers suite (test/workers/ppt-codec.test.ts) now also exercises a write-then-read round trip inside a real workerd isolate, proving the write path (and archive-codec's CFB writer beneath it) is genuinely Worker-isomorphic too.

As a side effect, src/test-support/records.ts's byte builders (used by every existing read-path fixture) are promoted into record/write.ts as real production code, so fixtures and genuinely written files are now built by identical code rather than two copies of the same logic.

What's out of scope (tracked, not silently dropped)

Documented exhaustively in the package README's new "What it does not write yet" section: images/tables/OLE embeddings, grouped/rotated shapes, per-shape text insets and autofit, masters/layouts/scheme colours, speaker notes and document metadata, hyperlinks/bullets/spacing/margins/list numbering identity, strike (no [MS-PPT] TextCFException bit exists for it at all), and animations/transitions/comments.

Test plan

  • pnpm exec turbo run _lint _typecheck _test _test:workers --filter=ppt-codec — green
  • pnpm exec turbo run _lint _typecheck (full workspace sanity pass) — green
  • CI (will confirm once triggered)

@Mearman
Mearman marked this pull request as ready for review September 3, 2026 12:20
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-03T12:28:27.429589Z 8143635 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

record/write.ts now owns the atom/container builders and little-endian
byte primitives that src/test-support/records.ts previously duplicated
for fixtures alone. Every test file and test-support/presentation.ts
import writeAtom/writeContainer (aliased to the atom/container names
they already used) directly from record/write.ts instead, so a test
fixture and a genuinely written record are built by identical code
with nothing left to drift between two copies.
Adds a genuine [MS-PPT] write path alongside the existing reader: one
slide per input slide, with plain text-box shapes carrying paragraph
text, character formatting (bold/italic/underline, font family, size,
literal RGB colour), and paragraph alignment/list level, wrapped in a
real [MS-CFB] compound file via archive-codec's writeCompoundFile.

Every record byte layout mirrors the equivalent read function's own
field order exactly, so round-tripping written bytes back through the
existing, independently-built reader (writePptContent -> readPptContent,
writePpt -> readPpt) is the primary correctness check. Content this
writer cannot express throws PptUnsupportedContentError: a
non-presentation document, or slides that do not share one size, since
DocumentAtom states exactly one slide size for the whole presentation.
A block kind the OfficeArt shape tree has no room for (image, table,
embeddedObject, a construct marker) is excluded from the written text
body rather than erroring, the same documented-gap convention the
reader already applies to its own unsupported constructs.

Scope deliberately narrower than the reader: no images, tables, OLE
embeddings, masters, layouts, scheme colours, speaker notes, or
document metadata. Text always goes directly on a shape's own
OfficeArtClientTextbox rather than through the OutlineTextRefAtom
placeholder indirection, since every shape this writer produces is a
plain text box with no placeholder role.
Adds a "Writing a document" usage section alongside the existing
"Reading a document" one, and "What it writes"/"What it does not
write yet" tables mirroring the read-side pair exactly, so a caller
can tell from the README alone whether a construct it cares about
round-trips. Updates the Architecture module table with every new
write-side module, the Conventions/Specification sections to note the
writer follows the same throw-on-malformed discipline (via
PptUnsupportedContentError on the write side), and the workspace root
README's package table and conversion-engine note, which previously
described this package as read-only.
@Mearman
Mearman force-pushed the worktree-agent-a27bf3e0aab3fe8cc branch from 8143635 to 350186c Compare September 3, 2026 12:21
@Mearman
Mearman merged commit 582233e into main Sep 3, 2026
17 checks passed
@Mearman
Mearman deleted the worktree-agent-a27bf3e0aab3fe8cc branch September 3, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant