Skip to content

pptx: embed a subset of the font program, not the whole family #563

Description

@DemchaAV

Since bundled families are embedded (#549), a deck that sets a few words of a script carries the entire font program for it. PptxEmbeddedFont.wrap reads the source with readAllBytes() and puts it into the EOT container verbatim; PptxRenderEnvironment.embedFontFamily already narrows to the faces a render used, but nothing narrows a face to the glyphs it drew.

Measured

assets/readme/examples/maven-banner.pptx — five slides, one of which sets a single word in each of five scripts:

part raw in the package
Gothic A1 2 295 566 688 556
Amiri 431 266 210 229
Noto Sans Georgian 232 190 154 560
Noto Sans Armenian 222 138 136 019
David Libre 127 914 64 154

The deck goes from 41 KB to 1.3 MB, and every byte of the difference is font data. The same document as a PDF grows 16 KB, because PDFBox embeds a subset of the glyphs actually drawn. One Korean word costs 688 KB.

The tradeoff, which is the real question

This backend's promise is an editable deck, and a subset embed is editable only for the characters already on the slide: type a new Hebrew word into a subset-embedded frame and the glyph is not there. PowerPoint offers exactly this choice for the same reason ("embed only the characters used" vs "embed all characters"), and picks the smaller one by default only for read-only sharing.

So the shape is probably a choice rather than a change — subset by default with an opt-out, or embed whole by default with an opt-in, decided on which failure a reader meets more often. Worth answering before the mechanism is written.

Notes for whoever takes it

  • The EOT header carries fontDataSize and the head table's checkSumAdjustment; both are derived from the data in PptxEmbeddedFont, so a subset flows through as long as the subset is produced before wrap.
  • fsType licence bits are already checked, and subsetting does not change what they permit.
  • The PDF backend's subsetting is PDFBox's; the seam to reuse is the glyph set a render collected, which is per-backend today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions