Skip to content

Honor blipFill <a:stretch><a:fillRect> cover crop on PPTX import#297

Merged
hackerwins merged 1 commit into
mainfrom
fix/pptx-blipfill-fillrect-crop
May 25, 2026
Merged

Honor blipFill <a:stretch><a:fillRect> cover crop on PPTX import#297
hackerwins merged 1 commit into
mainfrom
fix/pptx-blipfill-fillrect-crop

Conversation

@hackerwins

@hackerwins hackerwins commented May 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Importing a .pptx where a photo is placed as a <p:sp> freeform shape with <a:blipFill> distorted the image when the shape's frame aspect ratio differed from the photo's. PowerPoint reconciles the mismatch with a negative <a:stretch><a:fillRect> (its "Fill" / cover crop): it scales the image past the shape bounds and the shape clips it. Our importer read only <a:srcRect> (source crop) and ignored <a:stretch><a:fillRect>, so with no crop the renderer stretched the whole image into the frame — squishing it.

Repro: "Blue Green Colorful Daycare Center Presentation.pptx", slide 3 — a 1200×1800 (2:3 portrait) photo placed in a square frame via fillRect l="-31963" t="-36905" r="-9496" b="-75284". The fill-region AR works out to exactly the photo's native 2:3, so PowerPoint shows it undistorted; we squished it. Slide 8 has 3 more.

Fix

A negative/zero fillRect (cover case) is mathematically the dual of a source crop, which the model and renderer already support. parseStretchFillRect() in image.ts derives the equivalent source Crop when <a:srcRect> yields none:

l,t,r,b = insets / 100_000
fw = 1 - l - r ; fh = 1 - t - b
crop = { x: -l/fw, y: -t/fh, w: 1/fw, h: 1/fh }

Applied only when the result stays within [0,1] (cover). Default all-zero fillRect → no-op; positive-inset/letterbox fillRect → falls back to full stretch. No model or renderer change — reuses the existing Crop pipeline (so backgrounds via <p:bgPr> benefit too).

Limitations (accepted for v1)

  • srcRect takes precedence; srcRect + fillRect are not composed.
  • Positive-inset (letterbox) fillRects fall back to full stretch.
  • <a:tile> fills unchanged; non-rect freeform clip paths still not honored (pre-existing).

Note

Only affects new imports — an already-imported document must be re-imported to pick up the corrected crop.

Test plan

  • pnpm --filter @wafflebase/slides test — 1301 pass, incl. 5 new image.test.ts cases (cover crop with real slide-3 values, all-zero no-op, positive-inset skip, degenerate fw<=0 skip, srcRect precedence).
  • pnpm verify:fast — green.
  • End-to-end verified against the real deck (temporary jsdom test, since removed): slide 3 yields exactly 1 cropped image (the photo, nested in a <p:grpSp>) with crop = {x:0.226, y:0.174, w:0.707, h:0.471}; cropped-source pixel AR ≈ 1.0 = the square frame ⇒ no distortion.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed image distortion in PPTX imports by properly handling image fill cropping configurations.
  • Documentation

    • Enhanced PPTX import documentation with additional image fill mapping guidelines.
  • Tests

    • Expanded test coverage for image cropping behavior in PPTX file imports.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hackerwins, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 1 review of capacity. Refill in 49 minutes and 35 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 106091c3-b698-4c8e-bec7-45c1f9edba2e

📥 Commits

Reviewing files that changed from the base of the PR and between bf91949 and a24bdf2.

📒 Files selected for processing (5)
  • docs/design/slides/slides-themes-layouts-import.md
  • docs/tasks/README.md
  • docs/tasks/active/20260525-pptx-blipfill-fillrect-crop-todo.md
  • packages/slides/src/import/pptx/image.ts
  • packages/slides/test/import/pptx/image.test.ts
📝 Walkthrough

Walkthrough

This PR adds support for PPTX image cover-crop via <a:stretch><a:fillRect> negative insets. parseBlipFill now falls back to parseStretchFillRect when <a:srcRect> is absent, deriving an equivalent normalized crop from fillRect insets while rejecting letterbox (positive) and out-of-range cases. Five test cases validate the behavior across negative, zero, positive, degenerate, and srcRect-precedence scenarios.

Changes

PPTX blipFill fillRect Crop Support

Layer / File(s) Summary
Requirements and task documentation
docs/design/slides/slides-themes-layouts-import.md, docs/tasks/README.md, docs/tasks/active/20260525-pptx-blipfill-fillrect-crop-todo.md
Design doc rows, task index entry, and detailed task documentation describe the image squish problem (srcRect-only reading), root cause (parseBlipFill ignoring stretch/fillRect), fix approach (fallback from srcRect to fillRect for cover-crop), accepted v1 limitations (no composition, non-rect clip paths, tile unchanged), and E2E verification against a reference slide deck.
parseBlipFill fallback and parseStretchFillRect helper
packages/slides/src/import/pptx/image.ts
parseBlipFill's crop resolution prefers <a:srcRect> and falls back to parseStretchFillRect for fillRect-based cover cropping. New parseStretchFillRect helper converts <a:fillRect> insets into normalized source Crop (only when valid sub-rectangles within [0,1]); rejects letterbox (positive insets) and out-of-range cases.
Test coverage for fillRect and srcRect precedence
packages/slides/test/import/pptx/image.test.ts
Five new parsePic test cases validate: (1) negative fillRect insets derive cover crop with expected normalized values, (2) all-zero fillRect yields no crop, (3) positive inset letterbox yields no crop, (4) degenerate insets yield no crop, (5) srcRect takes precedence when both fillRect and srcRect are present.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • wafflebase/wafflebase#284: The main PR's changes to parseBlipFill crop resolution (srcRect fallback to stretch/fillRect) directly affect the retrieved PR's new shape-level <a:blipFill>ImageElement path since shape.ts builds the image via the same parseBlipFill helper.
  • wafflebase/wafflebase#243: The main PR extends the PPTX image import logic in packages/slides/src/import/pptx/image.ts by enhancing crop resolution (parseBlipFill now falls back from <a:srcRect> to <a:stretch><a:fillRect>), building directly on the retrieved PR's existing PPTX image/crop parsing in that same module and its tests.

Poem

A rabbit hops through fillRect bounds,
No more squished crops on slide grounds!
From srcRect peaks to stretch's fall,
Cover-crop logic handles it all. 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main feature being added: support for PPTX blipFill cover crop from fillRect attributes during import.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pptx-blipfill-fillrect-crop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hackerwins
hackerwins force-pushed the fix/pptx-blipfill-fillrect-crop branch from bf91949 to 3709cbd Compare May 25, 2026 11:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/tasks/active/20260525-pptx-blipfill-fillrect-crop-todo.md`:
- Around line 33-37: The fenced code block containing "l,t,r,b = insets /
100000" etc. is missing a language tag and triggers markdownlint MD040; update
the opening fence from ``` to include a language token (e.g., ```text or
```python) so the block is fenced with a language, keeping the block content
unchanged and ensuring markdownlint passes.
- Around line 51-52: Update the checklist entry that currently references
`test/import/pptx/image.test.ts` to use the repository-relative path
`packages/slides/test/import/pptx/image.test.ts` so the checklist accurately
points to the test file included in this PR; edit the checkbox line in the
markdown to replace the old path with the new one.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ab1fa4de-3809-44a2-a584-6272c1effd7d

📥 Commits

Reviewing files that changed from the base of the PR and between 1d7a801 and bf91949.

📒 Files selected for processing (5)
  • docs/design/slides/slides-themes-layouts-import.md
  • docs/tasks/README.md
  • docs/tasks/active/20260525-pptx-blipfill-fillrect-crop-todo.md
  • packages/slides/src/import/pptx/image.ts
  • packages/slides/test/import/pptx/image.test.ts

Comment thread docs/tasks/active/20260525-pptx-blipfill-fillrect-crop-todo.md Outdated
Comment thread docs/tasks/active/20260525-pptx-blipfill-fillrect-crop-todo.md Outdated
@github-actions

github-actions Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 216.8s

Lane Status Duration
tokens:build ✅ pass 2.2s
sheets:build ✅ pass 12.2s
docs:build ✅ pass 10.9s
slides:build ✅ pass 12.5s
verify:fast ✅ pass 139.2s
frontend:build ✅ pass 17.0s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.7s
cli:build ✅ pass 2.0s
verify:entropy ✅ pass 15.7s

Verification: verify:integration

Result: ✅ PASS

PowerPoint places photos as <p:sp> freeform shapes filled with
<a:blipFill>. When the shape's frame aspect ratio differs from the
photo's, PowerPoint reconciles them with a negative <a:stretch>
<a:fillRect> (Fill/cover crop): it scales the image past the shape
bounds and the shape clips it.

The importer's parseBlipFill read only <a:srcRect> and ignored
<a:stretch><a:fillRect>, so with no crop the renderer stretched the
whole image into the frame, squishing it (e.g. a 2:3 portrait into a
square shape on slide 3 of the daycare deck).

Derive the equivalent source Crop from the negative fillRect (cover
case only — within [0,1]); the default all-zero fillRect is a no-op and
positive-inset/letterbox fillRects fall back to a full stretch. Reuses
the existing Crop pipeline, so no model or renderer change.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@codecov

codecov Bot commented May 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@hackerwins
hackerwins force-pushed the fix/pptx-blipfill-fillrect-crop branch from 3709cbd to a24bdf2 Compare May 25, 2026 11:16
@hackerwins
hackerwins merged commit c664e9f into main May 25, 2026
1 check passed
@hackerwins
hackerwins deleted the fix/pptx-blipfill-fillrect-crop branch May 25, 2026 11:21
hackerwins added a commit that referenced this pull request May 25, 2026
These tasks landed on main per recent commit log: docker-publish-arm64
(#296), import-progress-toast (#299), pptx-blipfill-fillrect-crop (#297),
release-v0.4.2 (#295), slides-textbox-autogrow (cf83a49), and
slides-textbox-edit-theme-color (#300). Move their docs to
archive/2026/05 and regenerate the task indexes.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
hackerwins added a commit that referenced this pull request May 25, 2026
These tasks landed on main per recent commit log: docker-publish-arm64
(#296), import-progress-toast (#299), pptx-blipfill-fillrect-crop (#297),
release-v0.4.2 (#295), slides-textbox-autogrow (cf83a49), and
slides-textbox-edit-theme-color (#300). Move their docs to
archive/2026/05 and regenerate the task indexes.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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