From 39140829568046e6600735c7549556d4dc787651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigrid=20Solveig=20Hafl=C3=ADnud=C3=B3ttir?= Date: Thu, 1 Dec 2022 16:00:13 +0100 Subject: [PATCH] [feature] add webp support (#4) --- go.mod | 3 +- go.sum | 25 ++++++++++- images/pjw-clean.webp | Bin 0 -> 15970 bytes images/pjw.webp | Bin 0 -> 15970 bytes jpeg.go | 2 +- terminator.go | 14 ++++++ terminator_test.go | 36 +++++++++++++++ webp.go | 101 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 images/pjw-clean.webp create mode 100644 images/pjw.webp create mode 100644 webp.go diff --git a/go.mod b/go.mod index e71d9b7..855b0c1 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/dsoprea/go-png-image-structure/v2 v2.0.0-20210512210324-29b889a6093d github.com/stretchr/testify v1.7.0 github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe + golang.org/x/image v0.1.0 ) require ( @@ -19,7 +20,7 @@ require ( github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 3e96ef0..3c0af52 100644 --- a/go.sum +++ b/go.sum @@ -36,20 +36,43 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe h1:ksl2oCx/Qo8sNDc3Grb8WGKBM9nkvhCm25uvlT86azE= github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= +golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/images/pjw-clean.webp b/images/pjw-clean.webp new file mode 100644 index 0000000000000000000000000000000000000000..375b0f47e76237663d2b0bb0ac234a796d176900 GIT binary patch literal 15970 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 I54A=5Uq5L-DgXcg literal 0 HcmV?d00001 diff --git a/jpeg.go b/jpeg.go index aa6940a..576c4e4 100644 --- a/jpeg.go +++ b/jpeg.go @@ -91,7 +91,7 @@ func (v *jpegVisitor) HandleSegment(segmentMarker byte, _ string, _ int, _ bool) if segmentMarker == jpegstructure.MARKER_EOI { // take account of the last 2 bytes taken up by the EOI eoiLength := 2 - + // this is the total file size we will // have written including the EOI willHaveWritten := v.writtenTotalBytes + eoiLength diff --git a/terminator.go b/terminator.go index 575bb1e..9d9e6e7 100644 --- a/terminator.go +++ b/terminator.go @@ -43,6 +43,8 @@ func Terminate(in io.Reader, fileSize int, mediaType string) (io.Reader, error) switch mediaType { case "image/jpeg", "jpeg", "jpg": err = terminateJpeg(scanner, pipeWriter, fileSize) + case "image/webp", "webp": + err = terminateWebp(scanner, pipeWriter) case "image/png", "png": // for pngs we need to skip the header bytes, so read them in // and check we're really dealing with a png here @@ -86,6 +88,18 @@ func terminateJpeg(scanner *bufio.Scanner, writer io.WriteCloser, expectedFileSi return nil } +func terminateWebp(scanner *bufio.Scanner, writer io.WriteCloser) error { + v := &webpVisitor{ + writer: writer, + } + + // use the webp visitor's 'split' function, which satisfies the bufio.SplitFunc interface + scanner.Split(v.split) + + scanAndClose(scanner, writer) + return nil +} + func terminatePng(scanner *bufio.Scanner, writer io.WriteCloser) error { ps := pngstructure.NewPngSplitter() diff --git a/terminator_test.go b/terminator_test.go index b8eaeb9..2558b9b 100644 --- a/terminator_test.go +++ b/terminator_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/suite" terminator "github.com/superseriousbusiness/exif-terminator" + "golang.org/x/image/webp" ) type TerminatorTestSuite struct { @@ -174,6 +175,41 @@ func (suite *TerminatorTestSuite) TestTerminateTurnip() { suite.EqualValues(turnipClean, b) } +func (suite *TerminatorTestSuite) TestTerminatePJW() { + pjw, err := os.Open("./images/pjw.webp") + if err != nil { + panic(err) + } + defer pjw.Close() + + stat, err := pjw.Stat() + if err != nil { + panic(err) + } + + originalSize := int(stat.Size()) + + out, err := terminator.Terminate(pjw, originalSize, "webp") + suite.NoError(err) + + // we should be able to get some bytes back from the returned reader + b, err := io.ReadAll(out) + suite.NoError(err) + suite.NotEmpty(b) + + // the processed image should have the same size as the initial image + suite.EqualValues(originalSize, len(b)) + + // should be decodable as a webp + _, err = webp.Decode(bytes.NewBuffer(b)) + suite.NoError(err) + + // bytes should be the same as the clean image + pjwClean, err := os.ReadFile("./images/pjw-clean.webp") + suite.NoError(err) + suite.EqualValues(pjwClean, b) +} + func (suite *TerminatorTestSuite) TestTerminatePanorama() { panorama, err := os.Open("./images/exif-panorama.jpg") if err != nil { diff --git a/webp.go b/webp.go new file mode 100644 index 0000000..392c487 --- /dev/null +++ b/webp.go @@ -0,0 +1,101 @@ +/* + exif-terminator + Copyright (C) 2022 SuperSeriousBusiness admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package terminator + +import ( + "encoding/binary" + "errors" + "io" +) + +const ( + riffHeaderSize = 4 * 3 +) + +var ( + riffHeader = [4]byte{'R', 'I', 'F', 'F'} + webpHeader = [4]byte{'W', 'E', 'B', 'P'} + exifFourcc = [4]byte{'E', 'X', 'I', 'F'} + xmpFourcc = [4]byte{'X', 'M', 'P', ' '} + + errNoRiffHeader = errors.New("no RIFF header") + errNoWebpHeader = errors.New("not a WEBP file") +) + +type webpVisitor struct { + writer io.Writer + doneHeader bool +} + +func fourCC(b []byte) [4]byte { + return [4]byte{b[0], b[1], b[2], b[3]} +} + +func (v *webpVisitor) split(data []byte, atEOF bool) (advance int, token []byte, err error) { + // parse/write the header first + if !v.doneHeader { + if len(data) < riffHeaderSize { + // need the full header + return + } + if fourCC(data) != riffHeader { + err = errNoRiffHeader + return + } + if fourCC(data[8:]) != webpHeader { + err = errNoWebpHeader + return + } + if _, err = v.writer.Write(data[:riffHeaderSize]); err != nil { + return + } + advance += riffHeaderSize + data = data[riffHeaderSize:] + v.doneHeader = true + } + + // need enough for fourcc and size + if len(data) < 8 { + return + } + size := int64(binary.LittleEndian.Uint32(data[4:])) + if (size & 1) != 0 { + // odd chunk size - extra padding byte + size++ + } + // wait until there is enough + if int64(len(data)-8) < size { + return + } + + fourcc := fourCC(data) + rawChunkData := data[8 : 8+size] + if fourcc == exifFourcc || fourcc == xmpFourcc { + // replace exif/xmp with blank + rawChunkData = make([]byte, size) + } + + if _, err = v.writer.Write(data[:8]); err == nil { + if _, err = v.writer.Write(rawChunkData); err == nil { + advance += 8 + int(size) + } + } + + return +}