Skip to content

fix: auto-fit PPTX table export to the slide - #238

Merged
RDMillen merged 1 commit into
mainfrom
fix/pptx-table-autofit
Sep 1, 2026
Merged

fix: auto-fit PPTX table export to the slide#238
RDMillen merged 1 commit into
mainfrom
fix/pptx-table-autofit

Conversation

@RDMillen

@RDMillen RDMillen commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Problem

A Markdown slide with body text and a 4-column, 7-row table renders fine in the
Kova preview but, when exported to PPTX, the table overruns the bottom of the
slide and its rows come out misshapen.

The preview keeps it in bounds because every body pane is an OverflowPane that
measures the rendered height and zoom-scales the content to fit. The exporter has
no equivalent: addTable handed PptxGenJS a fixed area and a fixed 14pt font
with autoPage: false, in which mode PptxGenJS does no wrapping maths at all -
it emits <a:tr h="{h / rowCount}"> and leaves layout to PowerPoint. a:tr/@h
is a minimum, so PowerPoint grows every row to fit its text and never shrinks
the font, and the columns are split equally regardless of content, so a
long-text column wraps into tall, uneven rows.

Change

New fitTableToArea() helper, the export-time analogue of OverflowPane:

  1. Content-weighted column widths, each clamped to 8-50% of the width then
    normalised, replacing PptxGenJS's equal split.
  2. Estimates wrapped line count per cell and steps the font size from 14pt down
    to a 7pt floor until the estimated table height fits its area.
  3. Passes explicit per-row heights and total height through, so PowerPoint
    renders close to what was computed rather than growing rows off the slide.
  4. If even 7pt cannot fit, row heights are squeezed so the frame still lands on
    the slide and a warning is surfaced through the export's warnings[].

addTable now returns the height it consumed so the caller anchors the table
caption directly under the fitted table. Cell margins set to narrow,
valign: 'top' set explicitly.

Observed on the reported case (body text plus a 4x7 table): shrinks to about
8pt, the description column gets roughly 5.1" and the owner column about 0.9",
total height about 1.8" - fits under the paragraph with no overflow.

Out of scope

The textFrac heuristic that decides how much of the body a table gets when
mixed with text is untouched; it is a little generous to the text, so a table
next to a short paragraph can end up smaller than necessary. Worth a follow-up.

Tests

  • New exportPptxTableFit.test.ts: dense table fits within the slide and shrinks
    below 14pt; small table stays at 14pt with equal columns; wide-content column
    gets more than twice the width of a short one; oversized table clamps at 7pt
    and warns.
  • tsc clean; full export suite and layout tests pass. Two pre-existing failures
    in src/engine/sheet/__tests__/evaluate.test.ts are unrelated (confirmed by
    stashing this change).

🤖 Generated with Claude Code

PptxGenJS in autoPage:false mode does no wrapping maths — it emits
<a:tr h="{h/rows}"> (a minimum PowerPoint grows to fit the text) with
equal-width columns, so a dense table ran off the slide and wrapped into
lopsided rows, unlike the live preview which zoom-scales it to fit.

Add fitTableToArea(): content-weighted column widths, an estimated
wrapped height, and a font-size step-down (14pt to a 7pt floor) until the
table fits its area, with explicit per-row heights handed to PowerPoint.
When even the minimum font can't fit, the rows are squeezed so the frame
still lands on the slide and a warning is surfaced. addTable now returns
the height consumed so the caption anchors under the fitted table.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@RDMillen
RDMillen merged commit b0eb8d7 into main Sep 1, 2026
2 checks passed
@RDMillen
RDMillen deleted the fix/pptx-table-autofit branch September 1, 2026 20:41
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