fix(pptx): clamp the clip raster scale and surface rasterized regions#442
Merged
Conversation
The clip raster fallback aims for a 2048-pixel long edge, but that ratio was only capped from above. A clip region wider than 2048 pt therefore drove the scale below 1.0 and rasterized *below* native size, while the picture stayed anchored at the full clip size — an A0-scale composite landed near 44 DPI and read as visibly blurry. The inner Math.max guarded the divisor, not the result. Clamp the scale between native size and 4x, behind named constants that say what each bound is for. Decks whose clip regions fit a normal page are unaffected: their scale already saturated at the upper cap, which is also why no existing test covered this branch — every other clip test uses a page a few hundred points wide. Also make the fallback's cost visible. Each rasterized region is a full sub-render through the PDF backend and loses text editability inside its bounds, yet nothing reported how often it happened. The render environment now counts rasterized regions and their pixel volume, and emits one DEBUG line per pass on com.demcha.compose.engine.render — silent when nothing was rasterized, so an all-vector deck adds no noise. It is wired into the two paths that can reach the clip fallback; the raster-slide path turns the whole page into one picture and never gets there. Tests: a clip box on a 3400x2400 pt page, asserting the picture is published at no less than native resolution. The fixture asserts its own premise — that the clip really exceeds 2048 pt — so it cannot silently stop exercising the branch. Removing only the floor turns it red. Verified: full reactor clean verify green (1519 tests, 0 failures). Capability matrix updated in the same change, per the repository rule.
The clamp removes the implicit ~17MB cap a downscaled raster used to impose, so a very large clip region now costs transient memory proportional to its size. That is the intended trade — blur is worse than a transient allocation, and the floor only engages above 2048pt — but the capability matrix described the resolution change without its resource consequence.
DemchaAV
force-pushed
the
fix/pptx-clip-raster-clamp
branch
from
July 25, 2026 12:34
a5d037c to
f49e99c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The clip raster fallback aims for a 2048-pixel long edge, but that ratio was only capped from above:
The inner
Math.maxguards the divisor, not the result. A clip region wider than 2048 pt therefore drove the scale below 1.0 and rasterized below native size — while the picture stayed anchored at the full clip size regardless. A 3370 pt box landed at ~44 DPI, a 4000 pt box at ~37, and the deck showed a visibly blurred composite with no warning.No existing test caught it because every other clip test uses a page a few hundred points wide, where the ratio saturates at the upper cap — the branch was unreachable from the suite.
What changed
CLIP_RASTER_TARGET_PIXELS,CLIP_RASTER_MAX_SCALE,CLIP_RASTER_MIN_SCALE) that state what each bound is for. Decks whose clip regions fit a normal page are unaffected — their scale already saturated at the cap.PptxRenderEnvironmentnow counts rasterized regions and their pixel volume and emits oneDEBUGline per pass oncom.demcha.compose.engine.render. Silent when nothing was rasterized, so an all-vector deck adds no noise.The summary is wired into the two paths that can reach the clip fallback. The raster-slide path turns the whole page into one picture and never gets there, so a summary there would always report zero.
Verification
./mvnw -B -ntp clean verify— BUILD SUCCESS, 1519 tests (from 1518), 0 failures.clipFragment.width() > 2048.0), so it cannot silently stop exercising the branch if the page size is ever edited.docs/architecture/backend-capability-matrix.mdupdated in the same change, per the repository rule.Tradeoff
Raising the 2048 target instead would inflate every ordinary clip; the target is a memory ceiling for the common case. The floor engages only above 2048 pt, where the alternative is shipping a blurred picture. Cost stays bounded — the long edge becomes
max(2048, clipWidth)px for a render the caller explicitly asked for at that size.