2019-10-10 15:20:25 +02:00
|
|
|
package imagesize
|
2019-09-27 13:49:52 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
tiffLeHeader = []byte("II\x2A\x00")
|
|
|
|
tiffBeHeader = []byte("MM\x00\x2A")
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
tiffDtByte = 1
|
|
|
|
tiffDtShort = 3
|
|
|
|
tiffDtLong = 4
|
|
|
|
|
|
|
|
tiffImageWidth = 256
|
|
|
|
tiffImageLength = 257
|
|
|
|
)
|
|
|
|
|
|
|
|
type tiffReader interface {
|
|
|
|
io.Reader
|
|
|
|
Discard(n int) (discarded int, err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func asTiffReader(r io.Reader) tiffReader {
|
|
|
|
if rr, ok := r.(tiffReader); ok {
|
|
|
|
return rr
|
|
|
|
}
|
|
|
|
return bufio.NewReader(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
type TiffFormatError string
|
|
|
|
|
|
|
|
func (e TiffFormatError) Error() string { return "invalid TIFF format: " + string(e) }
|
|
|
|
|
|
|
|
func DecodeTiffMeta(rr io.Reader) (*Meta, error) {
|
|
|
|
var (
|
|
|
|
tmp [12]byte
|
|
|
|
byteOrder binary.ByteOrder
|
|
|
|
)
|
|
|
|
|
|
|
|
r := asTiffReader(rr)
|
|
|
|
|
|
|
|
if _, err := io.ReadFull(r, tmp[:8]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case bytes.Equal(tiffLeHeader, tmp[0:4]):
|
|
|
|
byteOrder = binary.LittleEndian
|
|
|
|
case bytes.Equal(tiffBeHeader, tmp[0:4]):
|
|
|
|
byteOrder = binary.BigEndian
|
|
|
|
default:
|
|
|
|
return nil, TiffFormatError("malformed header")
|
|
|
|
}
|
|
|
|
|
|
|
|
ifdOffset := int(byteOrder.Uint32(tmp[4:8]))
|
|
|
|
|
|
|
|
if _, err := r.Discard(ifdOffset - 8); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := io.ReadFull(r, tmp[0:2]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
numItems := int(byteOrder.Uint16(tmp[0:2]))
|
|
|
|
|
|
|
|
var width, height int
|
|
|
|
|
|
|
|
for i := 0; i < numItems; i++ {
|
|
|
|
if _, err := io.ReadFull(r, tmp[:]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
tag := byteOrder.Uint16(tmp[0:2])
|
|
|
|
|
|
|
|
if tag != tiffImageWidth && tag != tiffImageLength {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
datatype := byteOrder.Uint16(tmp[2:4])
|
|
|
|
|
|
|
|
var value int
|
|
|
|
|
|
|
|
switch datatype {
|
|
|
|
case tiffDtByte:
|
|
|
|
value = int(tmp[9])
|
|
|
|
case tiffDtShort:
|
|
|
|
value = int(byteOrder.Uint16(tmp[8:10]))
|
|
|
|
case tiffDtLong:
|
|
|
|
value = int(byteOrder.Uint32(tmp[8:12]))
|
|
|
|
default:
|
|
|
|
return nil, TiffFormatError("unsupported IFD entry datatype")
|
|
|
|
}
|
|
|
|
|
|
|
|
if tag == tiffImageWidth {
|
|
|
|
width = value
|
|
|
|
} else {
|
|
|
|
height = value
|
|
|
|
}
|
|
|
|
|
|
|
|
if width > 0 && height > 0 {
|
|
|
|
return &Meta{
|
|
|
|
Format: "tiff",
|
|
|
|
Width: width,
|
|
|
|
Height: height,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, TiffFormatError("image dimensions are not specified")
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
RegisterFormat(string(tiffLeHeader), DecodeTiffMeta)
|
|
|
|
RegisterFormat(string(tiffBeHeader), DecodeTiffMeta)
|
|
|
|
}
|