Skip to content

officecanvas: record a laid-out page as editable office objects - #299

Merged
tannevaled merged 1 commit into
mainfrom
feat/office-canvas
Oct 19, 2026
Merged

tannevaled merged 1 commit into
mainfrom
feat/office-canvas

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

officecanvas: a second paint.Canvas consumer beside pdfcanvas. PageLayout.PaintVector draws through it and it records the page as the objects an editable office slide is made of, format-neutral, in device pixels:

  • KindFill — filled outlines (Rect flagged when the outline is its box; rings keep their holes, EvenOdd);
  • KindGradient — the innermost clip's outline filled with a gradient: PaintVector's constant-opacity shadings of one CSS gradient are merged back into one stop list (positions mapped onto the box's projection for linear, radius fraction for radial), with transparent stops across the gaps — so uncover's "colour, then transparent" corner is one shape again;
  • KindText — one text box per line: consecutive glyph runs on one baseline join, the gap between words a space; runs split by face, size, colour, link (from PageLayout.Links), invisibility (text over a rasterised element → zero alpha) and letter-spacing (measured from the glyph positions);
  • KindImage — pictures, cropped to the clip's bounds (a background-size: cover image);
  • Page.Background — a fill covering the page before anything else.

Faces: family and style from the font's name table (Windows records preferred; the OS/2 weight/slant when the table calls every face "Regular", unless the family name carries the weight — "Lato Black" stays a family), embeddability from OS/2 fsType (installable or editable only). SFNT(font) reassembles the font file from its tables (offset table, directory with checksums, head.checkSumAdjustment) so a deck can embed the very font the slide was laid out with — tested: Inter round-trips through opentype.Parse. Shape.Render(scale) draws a gradient shape as an antialiased picture for formats whose gradients are poorer (ODF's two colours, DrawingML's circle-only radial).

Why here

Same pattern as pdfcanvas: the consumer of paint.Canvas lives next to it. It does not import go-ooxml or go-odf (the engine stays free of office libraries; they stay free of the engine); go-slides maps the neutral Page to both. go-slides PR: editable --pptx/--odp.

Verification (go-slides internal/cmd/officecheck)

LibreOffice 25.8 renders the editable decks to PDF, poppler to pages, compared with export.Rasterize: 0.04–1.9 % of pixels off by > 48 on the benchmark and oracle decks (glyph edges, different rasteriser), text extracted by pdftotext (2,240 words on 100 slides).

Bounds

SFNT refuses past 32 MiB; Render past 16 Mpx; the name parser bounds every record to the table.

MINOR → v0.10.0.

🤖 Generated with Claude Code

A second paint.Canvas consumer, beside pdfcanvas: PaintVector draws the same
layout through it, and it records what an editable slide is made of —
filled shapes, gradient shapes (PaintVector's constant-opacity shadings
merged back into one gradient, transparent across their gaps), one text box
per line (runs split by face, size, colour, link, letter-spacing; the gap
between words a space), pictures cropped to their clip — with the page
background and the faces used: family and style from the 'name' table (with
the OS/2 weight/slant for fonts that call every face Regular), embeddability
from fsType, and SFNT, which reassembles the font file from its tables so the
deck can embed the font the slide was laid out with. Shape.Render draws a
gradient as a picture for formats whose gradients are poorer (ODF's two
colours).

Format-neutral, in device pixels: go-slides maps it to go-ooxml and go-odf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit c391e1c into main Oct 19, 2026
7 checks passed
@tannevaled
tannevaled deleted the feat/office-canvas branch October 19, 2026 20:21
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