Pictures: WMF/EMF text, pattern and hatch brushes draw; ActiveX previews show (#336)

* ActiveX controls show their pictures

A slide's p:controls are read: each control with a p:pic (the image PowerPoint shows for it,
often a WMF) becomes a picture above the slide's shapes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Metafiles draw their text, pattern and hatch brushes

deckcraft-metafile (still kurbo + thiserror only, so it builds for wasm):
- Text: WMF EXTTEXTOUT/TEXTOUT and EMF EXTTEXTOUTW/EXTTEXTOUTA/SMALLTEXTOUT with the
  selected font (CREATEFONTINDIRECT, EXTCREATEFONTINDIRECTW: face, signed LOGFONT height,
  width, weight, italic, underline, strike-out, escapement, charset), text and background
  colour, background mode, TA_* alignment and TA_UPDATECP, the dx array (ETO_PDY pairs too),
  ETO_OPAQUE fills and ETO_CLIPPED clips. 8-bit strings decode through their charset
  (Windows-1252/1250/1251; symbol fonts keep their codes at U+F000; double-byte sets keep
  their advances). The neutral Item::Text carries a text-space-to-picture transform, so the
  mapping and escapement are folded in and glyphs are never mirrored.
- Brushes: WMF DIBCREATEPATTERNBRUSH, EMF CREATEDIBPATTERNBRUSHPT and CREATEMONOBRUSH, and
  BS_HATCHED, as Item::Pattern tiles in 96-dpi pixels; WMF PATBLT. The XOR / AND-pattern /
  XOR see-through shadow of old PowerPoint WMFs draws its pattern in the XOR colour.
- MFCOMMENT escapes with an embedded EMF are ignored: PowerPoint draws the WMF's records.
- Pattern pixels and text count against the byte budget; strings are capped.

deckcraft-render draws text through deckcraft-fonts with the face slide text would use
(document_face), symbol fonts mapped as bullets map them (deckcraft_text::symbol_char, now
public, with Webdings), per-character fallback, the file's advances, GDI cell metrics for
top/bottom alignment, underline/strike-out and synthetic bold/italic. Tiles below 1.5 output
pixels per cell are drawn as their average colour.

On the 24 corpus slides that show a metafile, mean abs diff against PowerPoint goes from
28.31 to 5.06 (with the OLE/ActiveX preview commits).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-12 22:40:56 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent d6ab3d10da
commit e28851d2f1
17 changed files with 1979 additions and 67 deletions
+3 -2
View File
@@ -38,7 +38,7 @@ PowerPoint opens and saves; Viewer = opens only).
|---|---|---|---|
| PNG, JPEG, GIF (animated), BMP, TIFF, WebP | yes | yes | `image` 0.25; animated GIFs play since #39 |
| SVG | yes (with PNG fallback) | **no** | The PNG fallback shows; SVG files can't be inserted |
| EMF / WMF | yes | partial | Drawn as vectors (paths, pens, bitmaps); text records inside are skipped |
| EMF / WMF | yes | partial | Drawn as vectors (paths, pens, pattern and hatch brushes, bitmaps, text in the document's fonts with the file's character advances); clip regions and EMF+ records are not drawn. Legacy OLE objects (VML preview) and ActiveX controls show their pictures |
| HEIC | yes (macOS) | no | |
| Audio: MP3, AAC/M4A, ALAC, WAV, AIFF, CAF, FLAC, Ogg Vorbis, Opus | yes (Ogg/Opus partly) | yes | Pure-Rust decoders in `crates/media`, `crates/opus` |
| WMA | yes (Windows) | embedded, not playable | |
@@ -93,11 +93,12 @@ the round trip even though DeckCraft doesn't model it.
embedded fonts, every transition and animation) in a corpus repo, with import → render → export
→ re-import checks in CI and a PowerPoint reference render for each (owner runs PowerPoint).
2. The round-trip losses above (#70, #74, #77).
3. SVG rendering, and text inside EMF/WMF.
3. SVG rendering, and clip regions inside EMF/WMF.
## Revision history
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | minor | EMF/WMF text and pattern brushes drawn; OLE (VML) and ActiveX previews read |
| 2026-10-12 | minor | Text warps rendered; their adjust values read and written |
| 2026-10-10 | major | Created from PowerPoint 16.113.4's `CFBundleDocumentTypes`, `crates/pptx` reader/writer and tests, and the open round-trip issues |
+8 -7
View File
@@ -20,7 +20,7 @@ revision history.
| 1 | PPTX never tested against a corpus of real decks | File format | B | 20–35 | [file-format-parity.md](file-format-parity.md) |
| 2 | Data loss and crashes on common paths (#60, #40, #59) | Stability | B | 6–12 | this file |
| 3 | PPTX round trip drops comment threads, media options, rewind (#70, #74, #77) | File format | B | 3–6 | [file-format-parity.md](file-format-parity.md) |
| 4 | SVG pictures don't render (EMF/WMF do) | File format | B | 8–14 | [file-format-parity.md](file-format-parity.md) |
| 4 | SVG pictures don't render (EMF/WMF do) | File format | B | 6–12 | [file-format-parity.md](file-format-parity.md) |
| 5 | No localization: English only | Localization | B (infra + 5 languages) | 55–90 | [localization-parity.md](localization-parity.md) |
| 6 | No printing | Feature | B | 5–8 | [file-format-parity.md](file-format-parity.md) |
| 7 | Fonts: only Regular, Bold and Italic faces (#69) | Feature | B | 3–5 | [ui-parity.md](ui-parity.md) |
@@ -77,12 +77,12 @@ Detail in [file-format-parity.md](file-format-parity.md).
### SVG pictures don't render
- **Evidence:** `crates/render/src/images.rs` decodes through `image` 0.25 (PNG, JPEG, WebP, GIF,
TIFF, BMP), and EMF/WMF through `deckcraft-metafile` (ported from WordCraft: paths, fills, pens
and bitmaps; text records are skipped). Nothing reads SVG; PowerPoint's Insert Icons stores SVG
with a PNG fallback.
- **Impact:** SVG icons show only their raster fallback, and SVG files can't be inserted. Text
drawn inside an EMF/WMF is missing.
- **Estimate:** 4–8 h (an SVG renderer through `resvg`-style crates); EMF text records 2–4 h.
TIFF, BMP), and EMF/WMF through `deckcraft-metafile` (ported from WordCraft: paths, fills, pens,
pattern and hatch brushes, bitmaps and text; clip regions and EMF+ records are not drawn).
Nothing reads SVG; PowerPoint's Insert Icons stores SVG with a PNG fallback.
- **Impact:** SVG icons show only their raster fallback, and SVG files can't be inserted. An
EMF/WMF that clips its drawing (EXTSELECTCLIPRGN, INTERSECTCLIPRECT) draws past its clip.
- **Estimate:** 4–8 h (an SVG renderer through `resvg`-style crates); EMF/WMF clip regions 2–4 h.
### Template, slide-show and macro variants are saved as plain `.pptx`
- **Evidence:** `crates/pptx/src/write/mod.rs` always writes the
@@ -256,6 +256,7 @@ Detail in [localization-parity.md](localization-parity.md).
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | minor | IME composition in slide text and notes, and CJK interface text with real glyphs (#101): the IME gap is now Korean on the web and table-cell underlines |
| 2026-10-12 | minor | EMF/WMF text, pattern and hatch brushes drawn; legacy OLE and ActiveX previews shown; the SVG gap keeps only EMF clip regions |
| 2026-10-12 | minor | Text warps render (all 40 presets, adjusts round-trip); the warp gap is now the gallery, command and handles |
| 2026-10-10 | minor | Noted that no gap blocks alpha (core-workflow gate passes) |
| 2026-10-10 | major | Created: every gap from the 2026-10-10 re-measure and the 16 open GitHub issues, ranked, with beta blockers marked |