Clamp padding to the canvas the artwork actually gets [patch] - #111
Merged
matt-edmondson merged 1 commit intoSep 23, 2026
Merged
Conversation
Arguments.Validate checks padding against --size, but sizing is downscale-only, so the canvas is min(trimmedSquareSize, size). Artwork that trims smaller than --size therefore got a canvas the validated padding did not fit on. --size 100 --padding 45 validates, and then artwork trimming to 80 square asks for 90 pixels of padding on an 80 pixel canvas, driving the content size to 80 - 90 = -10. Resize threw on the non-positive dimension. ProcessDirectory's broad per-file catch turned that into a silently skipped file, a Failed count and exit code 2, on a batch the user had configured exactly as the help text and README documented. EffectivePadding now clamps to (canvasSize - 1) / 2, which leaves at least one pixel of content on any canvas. Clamping is preferred over reporting the file as failed because the requested padding is not wrong, only unsatisfiable on that input, and a directory of assorted icon sizes would otherwise fail exactly the small ones. Padding that already fits is applied unchanged, so this engages only where the tool used to throw. The help text, README and CLAUDE.md all stated the old constraint as if --size were the bound, so all three are corrected rather than left to imply the check is complete. Fixes #110 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XH3MikiXVCii3abMojvibs
|
matt-edmondson
deleted the
claude/iconhelper-110-padding-effective-size
branch
September 23, 2026 00:03
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.



Fixes #110
Arguments.Validatecheckspadding < size / 2. But sizing is downscale-only, so the canvas ismin(trimmedSquareSize, size)— and the two differ for any artwork that trims smaller than--size:45 < 100/2, so this validates. Artwork trimming to 80 square then asks for 90 pixels of padding on an 80 pixel canvas, and
finalContentSizecomes out as80 - 90 = -10.ProcessDirectory's broad per-file catch turns that into a skipped file, a non-zeroFailedcount and exit code 2 — on a batch configured exactly as the help text and README documented.The fix, and why clamping rather than reporting
EffectivePadding(canvasSize, padding)clamps to(canvasSize - 1) / 2, which leaves at least one pixel of content on any canvas (an even canvas keeps 2, an odd one keeps 1).The issue offered two routes — clamp, or report a per-file "padding too large" error. This takes the clamp, because the requested padding is not wrong, it is only unsatisfiable on that particular input. A directory of assorted icon sizes is the normal case for this tool, and the reporting route would fail exactly the small ones while the large ones succeeded, which is the same partial-failure exit code the user is trying to get away from. Padding that already fits is applied unchanged, so the clamp engages only where the tool used to throw.
Scope
IconHelper/IconHelper.cs—EffectivePadding, called fromCropSquareAndResize. That is the whole behaviour change.IconHelper/Arguments.cs,README.md,CLAUDE.md— all three stated the constraint as though--sizewere the bound. Correcting the code without correcting them would leave three places asserting the check is complete when it is not.Tests
Four cases, at both layers.
ProcessImageTests.PaddingValidAgainstTheRequestedSizeStillWorksOnSmallerArtworkProcessImageTests.PaddingTooLargeForTheCanvasStillLeavesVisibleContentProcessImageTests.PaddingThatFitsIsAppliedExactlyAndNotClampedProcessDirectoryTests.SmallArtworkWithValidatedPaddingDoesNotFailTheBatchValidatepasses,Failedis 0, exit code is 0The batch-level test is the one that matters, because the throw was never visible as a throw — the broad catch converted it into a count. A
ProcessImagetest alone would prove the exception is gone without proving the user-facing symptom is.Confirmed the tests depend on the change by loosening the clamp bound to
Math.Min(padding, canvasSize)and re-running: 3 failed, 6 passed, with the original symptom back in the batch test's captured output:PaddingThatFitsIsAppliedExactlyAndNotClampedpasses either way by design — it is the guard against the clamp reaching inputs that were always valid, not evidence for the fix.Release build clean, zero warnings.
One environmental caveat
The 8
GoldMasterTestsfail in my container, and they fail identically on an unmodified tree — I stashed everything and re-ran to check: 69 tests / 8 failed before, 73 tests / 8 failed after, same 8 cases. The cause is the oneCLAUDE.mdalready documents: the fixtures are Git LFS pointers wheregit lfsis not installed, so ImageSharp rejects them asversion https://git-lfs.... They should pass on a runner with LFS.Worth flagging because the gold master matrix includes
(midtone-grey-shape, #3366CC, 96, 8)and(antialiased-circle, #FF8800, 64, 4), which are the committed padding cases — so CI is the first thing that will actually exercise whether the clamp leaves those two byte-identical. It should: both have padding well inside(canvasSize - 1) / 2for their canvases, soEffectivePaddingreturns the requested value untouched. If either moves, that is a real finding rather than noise.Not covered
Validate'spadding >= size / 2check is left as it is. It still catches the genuinely impossible request up front, and it is the only check available at argument-parsing time, since the effective canvas is not known until an individual file has been decoded and trimmed.🤖 Generated with Claude Code
https://claude.ai/code/session_01XH3MikiXVCii3abMojvibs
Generated by Claude Code