Skip to content

redact: decode a JPEG with the fleet's decoder, the one the reader is shown - #89

Merged
tannevaled merged 1 commit into
mainfrom
fix/decode-with-the-fleets-jpeg
Nov 11, 2026
Merged

tannevaled merged 1 commit into
mainfrom
fix/decode-with-the-fleets-jpeg

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

⛔ "What a reader would have seen" names a reader, and it is not the standard
library.
v0.38.0 decodes a redacted JPEG with image/jpeg.
go-pdfkit/render, which is what actually
shows the page, decodes /DCTDecode with github.com/go-images/jpeg: Go's own
decoder with a four-component picture's chroma upsampled the way libjpeg does
rather than by repeating each sample — the docs say so under
working-with-a-file/rendering. Writing back the output of a different
decoder makes the promise true of a picture nobody was shown.

The fork was already in the module graph as an indirect requirement, so this
costs one line in go.mod and nothing else: same Decode/DecodeConfig
signatures.

The first version of this comment was wrong

It claimed the swap saved linking a second decoder. go list -deps ./... says
otherwise, before and after: image/jpeg stays. Three packages pull it in —

package imports
go-gfx/gfx/codec image/jpeg, beside go-images/jpeg2000
go-images/images image/jpeg and github.com/go-images/jpeg
go-pdfkit/html2pdf image/jpeg

— and go-images/images, whose whole subject this is, imports both. That is
a wider divergence than this file and belongs to those repositories, so it is
recorded here and raised with them rather than reached into.

The test now cross-checks the two decoders

redactjpeg_test.go still encodes and decodes with image/jpeg while
redactimage.go decodes with the fork, so the byte-for-byte equality is also
the statement that they agree on a three-component picture — and the day they
stop agreeing it goes red and names the row and column. Importing the fork in
the test as well would have thrown that away.

Gates

gofmt -l . silent, go vet ./... silent, 100.0% of statements, 0
uncovered blocks of 3873
.

🤖 Generated with Claude Code

… shown

⛔ "WHAT A READER WOULD HAVE SEEN" NAMES A READER, AND IT IS NOT THE STANDARD
LIBRARY. v0.38.0 decodes a redacted JPEG with image/jpeg. go-pdfkit/render,
which is what actually shows the page, decodes /DCTDecode with
github.com/go-images/jpeg: Go's own decoder with a four-component picture's
chroma upsampled the way libjpeg does rather than by repeating each sample
(docs, working-with-a-file/rendering). Writing back the output of a DIFFERENT
decoder makes the promise true of a picture nobody was shown.

The fork was already in the module graph as an indirect requirement, so this
costs a line in go.mod and nothing else. Same Decode/DecodeConfig signatures.

⛔ AND THE FIRST VERSION OF THIS COMMENT WAS WRONG. It claimed the swap saved
linking a second decoder. `go list -deps ./...` says otherwise, before and
after: image/jpeg stays. Three packages pull it in —

    go-gfx/gfx/codec           image/jpeg (beside go-images/jpeg2000)
    go-images/images           image/jpeg AND github.com/go-images/jpeg
    go-pdfkit/html2pdf         image/jpeg

— and go-images/images, whose whole subject this is, imports BOTH. That is a
wider divergence than this file and belongs to those repositories, so it is
recorded here and raised with them rather than reached into.

⛔ THE TEST NOW CROSS-CHECKS THE TWO. redactjpeg_test.go still encodes and
decodes with image/jpeg while redactimage.go decodes with the fork, so the
byte-for-byte equality is ALSO the statement that they agree on a
three-component picture; the day they stop agreeing it goes red and names the
row and column. Importing the fork in the test as well would have thrown that
away.

gofmt clean, go vet clean, 100.0% of statements, 0 uncovered blocks of 3873.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit d330644 into main Nov 11, 2026
1 check passed
@tannevaled
tannevaled deleted the fix/decode-with-the-fleets-jpeg branch November 11, 2026 14:05
tannevaled added a commit that referenced this pull request Nov 12, 2026
… on purpose (#91)

⛔⛔ I WROTE TWO CLAIMS INTO A SHIPPED COMMENT WITHOUT CHECKING EITHER, and was
one command away from filing two issues in other organisations on the strength
of them.

The comment added in #89 said the import swap saved linking a second decoder —
already corrected in that pull request, `go list -deps` says image/jpeg stays
either way — and then that go-gfx/gfx/codec, go-images/images and
go-pdfkit/html2pdf importing image/jpeg is "a wider divergence than this file",
with go-images/images importing BOTH singled out as the worst of it.

Reading that dependency's source says otherwise, and says it deliberately. Its
package doc: "PNG and JPEG via the standard library". Its partial.go, which is
the only file importing the fork:

    ⛔ One divergence from [Decode], stated rather than hidden. On a COMPLETE
    file this returns the FORK's decode, and for a four-component JPEG that
    differs: the fork upsamples chroma the way libjpeg does, where the standard
    library repeats each sample. Decode's standard-library path is untouched,
    so anything relying on its exact bytes still gets them.

So the two decoders live there on purpose, the divergence is written down where
the divergence is made, and there is nothing to file. A tracker entry claiming
otherwise would have cost a maintainer a reading to refute.

⛔ WHAT SURVIVES THE CHECK IS THE CHANGE ITSELF. go-pdfkit/render's import list
holds github.com/go-images/jpeg and NOT image/jpeg, so decoding a redacted
picture with the fork really is decoding it the way the page is shown. That was
the reason; the rest was decoration I had not earned.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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