package imaging import ( "bytes" "encoding/binary" ) const ( riffHeaderSize = 12 riffChunkHeaderSize = 8 webpVP8X = "VP8X" webpEXIF = "EXIF" webpXMP = "XMP " webpEXIFFlag = 0x08 webpXMPFlag = 0x04 ) func stripWebP(data []byte) ([]byte, error) { if len(data) < riffHeaderSize || string(data[:4]) != "RIFF" || string(data[8:12]) != "WEBP" { return nil, errMalformedContainer } declared := int(binary.LittleEndian.Uint32(data[4:])) + riffChunkHeaderSize if declared > len(data) || declared < riffHeaderSize { return nil, errMalformedContainer } data = data[:declared] var chunks [][]byte orientation := uint16(uprightOrientation) for pos := riffHeaderSize; pos < len(data); { available := len(data) - pos - riffChunkHeaderSize if available < 0 { return nil, errMalformedContainer } size := binary.LittleEndian.Uint32(data[pos+4:]) padded := uint64(size) + uint64(size%2) if padded > uint64(available) { return nil, errMalformedContainer } kind := string(data[pos : pos+4]) payload := data[pos+riffChunkHeaderSize : pos+riffChunkHeaderSize+int(size)] end := pos + riffChunkHeaderSize + int(padded) switch kind { case webpEXIF: if found, ok := exifOrientation(payload); ok { orientation = found } case webpXMP: default: chunks = append(chunks, bytes.Clone(data[pos:end])) } pos = end } if len(chunks) == 0 { return nil, errMalformedContainer } if string(chunks[0][:4]) == webpVP8X { if len(chunks[0]) <= riffChunkHeaderSize { return nil, errMalformedContainer } flags := &chunks[0][riffChunkHeaderSize] *flags &^= webpEXIFFlag | webpXMPFlag if needsOrientation(orientation) { *flags |= webpEXIFFlag chunks = append(chunks, riffChunk(webpEXIF, orientationOnlyTIFF(orientation))) } } return riffContainer(chunks), nil } func riffChunk(kind string, payload []byte) []byte { chunk := append([]byte(kind), binary.LittleEndian.AppendUint32(nil, uint32(len(payload)))...) chunk = append(chunk, payload...) if len(payload)%2 == 1 { chunk = append(chunk, 0) } return chunk } func riffContainer(chunks [][]byte) []byte { body := []byte("WEBP") for _, chunk := range chunks { body = append(body, chunk...) } out := append([]byte("RIFF"), binary.LittleEndian.AppendUint32(nil, uint32(len(body)))...) return append(out, body...) }