summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/superseriousbusiness/exif-terminator/webp.go
blob: b050f38fcb900f7b554be3537efd4554c8bf9f19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
   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 <http://www.gnu.org/licenses/>.
*/

package terminator

import (
	"encoding/binary"
	"errors"
	"io"
)

const (
	riffHeader = "RIFF"
	webpHeader = "WEBP"
	exifFourcc = "EXIF"
	xmpFourcc  = "XMP "
)

var (
	errNoRiffHeader = errors.New("no RIFF header")
	errNoWebpHeader = errors.New("not a WEBP file")
	errInvalidChunk = errors.New("invalid chunk")
)

type webpVisitor struct {
	writer     io.Writer
	doneHeader bool
}

func (v *webpVisitor) split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	// parse/write the header first
	if !v.doneHeader {

		// const rifHeaderSize = 12
		if len(data) < 12 {
			if atEOF {
				err = errNoRiffHeader
			}
			return
		}

		if string(data[:4]) != riffHeader {
			err = errNoRiffHeader
			return
		}

		if string(data[8:12]) != webpHeader {
			err = errNoWebpHeader
			return
		}

		if _, err = v.writer.Write(data[:12]); err != nil {
			return
		}

		advance += 12
		data = data[12:]
		v.doneHeader = true
	}

	for {
		// 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
		}

		// replace exif/xmp with blank
		switch string(data[:4]) {
		case exifFourcc, xmpFourcc:
			clear(data[8 : 8+size])
		}

		if _, err = v.writer.Write(data[:8+size]); err != nil {
			return
		}

		advance += 8 + int(size)
		data = data[8+size:]
	}
}