From 0618838255744b812ea76a3d643e75fa258261c5 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Wed, 11 Nov 2026 10:20:31 +0100 Subject: [PATCH] redact: decode a JPEG with the fleet's decoder, the one the reader is shown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⛔ "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 --- go.mod | 2 +- redactimage.go | 15 ++++++++++++++- redactjpeg_test.go | 9 ++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 23d3d7a..37e2ba1 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.27.2 require ( github.com/go-gfx/gfx v0.41.0 github.com/go-images/images v0.3.0 + github.com/go-images/jpeg v0.3.0 github.com/go-ooxml/ooxml v0.7.0 github.com/go-ooxml/opc v0.2.1 github.com/go-pdfkit/document v0.2.0 @@ -28,7 +29,6 @@ require ( github.com/go-browserhttp/browserhttp v0.2.0 // indirect github.com/go-gfx/oksvg v0.2.0 // indirect github.com/go-images/gif v0.2.0 // indirect - github.com/go-images/jpeg v0.3.0 // indirect github.com/go-images/jpeg2000 v0.14.0 // indirect github.com/go-images/png v0.2.0 // indirect github.com/go-opentype/fonts v0.14.0 // indirect diff --git a/redactimage.go b/redactimage.go index cac5df2..a7f2e3d 100644 --- a/redactimage.go +++ b/redactimage.go @@ -5,8 +5,21 @@ import ( "fmt" "image" "image/draw" - "image/jpeg" + // ⛔ THE FLEET'S DECODER, NOT THE STANDARD LIBRARY'S. What this promises to + // write back is "what a reader would have seen", and the reader is + // go-pdfkit/render, which decodes /DCTDecode with this fork: Go's own + // image/jpeg with a four-component picture's chroma upsampled the way + // libjpeg does rather than by repeating each sample. Decoding here with a + // different decoder would make that promise true of a picture nobody was + // shown. + // + // ⛔ AND IT IS NOT ABOUT THE BINARY. This was first written claiming it + // saved linking a second decoder; `go list -deps` says otherwise, before + // and after. image/jpeg stays, because go-gfx/gfx/codec, go-images/images + // and go-pdfkit/html2pdf each import it — go-images/images imports BOTH. + // That is a wider divergence than this file, filed rather than fixed here. + "github.com/go-images/jpeg" "github.com/go-pdfkit/extract" "github.com/go-pdfkit/reader" ) diff --git a/redactjpeg_test.go b/redactjpeg_test.go index 575cd4e..7336326 100644 --- a/redactjpeg_test.go +++ b/redactjpeg_test.go @@ -130,7 +130,14 @@ var bottomQuarter = Redaction{Page: 1, Quads: []Quad{{ // // ⛔⛔ THE KEPT SAMPLES ARE COMPARED AGAINST THIS TEST'S OWN DECODE OF THE // ORIGINAL JPEG, BYTE FOR BYTE. That is the whole claim the lossless path -// makes. Asserting a colour instead would be asserting what an encoder does +// makes. +// +// ⛔ AND THE TWO DECODERS ARE NOT THE SAME ONE. This file encodes and decodes +// with the standard library's image/jpeg; redactimage.go decodes with +// go-images/jpeg, the fork go-pdfkit/render shows the reader. So the equality +// below is also the statement that the two agree on a three-component picture +// — and the day they stop agreeing, this goes red and names the row and column +// where. Importing the fork here as well would have thrown that away. Asserting a colour instead would be asserting what an encoder does // with it, which is a different subject and a moving one; and asserting "it // still looks about the same" would pass for a picture re-encoded into mud — // which is what re-encoding as a JPEG would have done to every block of it,