Files
RenewCMS/internal/infrastructure/imaging/pngMetadata.go
T

87 lines
2.1 KiB
Go

package imaging
import (
"bytes"
"encoding/binary"
"hash/crc32"
)
const (
pngChunkHeaderSize = 8
pngChunkCRCSize = 4
pngIHDR = "IHDR"
pngIEND = "IEND"
pngEXIF = "eXIf"
)
var pngSignature = []byte("\x89PNG\r\n\x1a\n")
var pngMetadataChunks = map[string]bool{
pngEXIF: true,
"tEXt": true,
"zTXt": true,
"iTXt": true,
"tIME": true,
}
func stripPNG(data []byte) ([]byte, error) {
if !bytes.HasPrefix(data, pngSignature) {
return nil, errMalformedContainer
}
out := bytes.Clone(pngSignature)
orientation := uint16(uprightOrientation)
insertAt := 0
for pos := len(pngSignature); pos < len(data); {
available := len(data) - pos - pngChunkHeaderSize - pngChunkCRCSize
if available < 0 {
return nil, errMalformedContainer
}
size := binary.BigEndian.Uint32(data[pos:])
if uint64(size) > uint64(available) {
return nil, errMalformedContainer
}
kind := string(data[pos+4 : pos+pngChunkHeaderSize])
payload := data[pos+pngChunkHeaderSize : pos+pngChunkHeaderSize+int(size)]
end := pos + pngChunkHeaderSize + len(payload) + pngChunkCRCSize
if pngMetadataChunks[kind] {
if kind == pngEXIF {
if found, ok := exifOrientation(payload); ok {
orientation = found
}
}
} else {
out = append(out, data[pos:end]...)
if kind == pngIHDR {
insertAt = len(out)
}
}
pos = end
if kind == pngIEND {
return withPNGOrientation(out, insertAt, orientation)
}
}
return nil, errMalformedContainer
}
func withPNGOrientation(out []byte, insertAt int, orientation uint16) ([]byte, error) {
if !needsOrientation(orientation) {
return out, nil
}
if insertAt == 0 {
return nil, errMalformedContainer
}
chunk := pngChunk(pngEXIF, orientationOnlyTIFF(orientation))
return append(out[:insertAt:insertAt], append(chunk, out[insertAt:]...)...), nil
}
func pngChunk(kind string, payload []byte) []byte {
chunk := binary.BigEndian.AppendUint32(nil, uint32(len(payload)))
chunk = append(chunk, kind...)
chunk = append(chunk, payload...)
return binary.BigEndian.AppendUint32(chunk, crc32.ChecksumIEEE(chunk[4:]))
}