simple tool to de-duplicate and arrange media.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
3.7 KiB

// Package tiff implements TIFF decoding as defined in TIFF 6.0 specification at
// http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
package tiff
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
)
// ReadAtReader is used when decoding Tiff tags and directories
type ReadAtReader interface {
io.Reader
io.ReaderAt
}
// Tiff provides access to a decoded tiff data structure.
type Tiff struct {
// Dirs is an ordered slice of the tiff's Image File Directories (IFDs).
// The IFD at index 0 is IFD0.
Dirs []*Dir
// The tiff's byte-encoding (i.e. big/little endian).
Order binary.ByteOrder
}
// Decode parses tiff-encoded data from r and returns a Tiff struct that
// reflects the structure and content of the tiff data. The first read from r
// should be the first byte of the tiff-encoded data and not necessarily the
// first byte of an os.File object.
func Decode(r io.Reader) (*Tiff, error) {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, errors.New("tiff: could not read data")
}
buf := bytes.NewReader(data)
t := new(Tiff)
// read byte order
bo := make([]byte, 2)
if _, err = io.ReadFull(buf, bo); err != nil {
return nil, errors.New("tiff: could not read tiff byte order")
}
if string(bo) == "II" {
t.Order = binary.LittleEndian
} else if string(bo) == "MM" {
t.Order = binary.BigEndian
} else {
return nil, errors.New("tiff: could not read tiff byte order")
}
// check for special tiff marker
var sp int16
err = binary.Read(buf, t.Order, &sp)
if err != nil || 42 != sp {
return nil, errors.New("tiff: could not find special tiff marker")
}
// load offset to first IFD
var offset int32
err = binary.Read(buf, t.Order, &offset)
if err != nil {
return nil, errors.New("tiff: could not read offset to first IFD")
}
// load IFD's
var d *Dir
prev := offset
for offset != 0 {
// seek to offset
_, err := buf.Seek(int64(offset), 0)
if err != nil {
return nil, errors.New("tiff: seek to IFD failed")
}
if buf.Len() == 0 {
return nil, errors.New("tiff: seek offset after EOF")
}
// load the dir
d, offset, err = DecodeDir(buf, t.Order)
if err != nil {
return nil, err
}
if offset == prev {
return nil, errors.New("tiff: recursive IFD")
}
prev = offset
t.Dirs = append(t.Dirs, d)
}
return t, nil
}
func (tf *Tiff) String() string {
var buf bytes.Buffer
fmt.Fprint(&buf, "Tiff{")
for _, d := range tf.Dirs {
fmt.Fprintf(&buf, "%s, ", d.String())
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// Dir provides access to the parsed content of a tiff Image File Directory (IFD).
type Dir struct {
Tags []*Tag
}
// DecodeDir parses a tiff-encoded IFD from r and returns a Dir object. offset
// is the offset to the next IFD. The first read from r should be at the first
// byte of the IFD. ReadAt offsets should generally be relative to the
// beginning of the tiff structure (not relative to the beginning of the IFD).
func DecodeDir(r ReadAtReader, order binary.ByteOrder) (d *Dir, offset int32, err error) {
d = new(Dir)
// get num of tags in ifd
var nTags int16
err = binary.Read(r, order, &nTags)
if err != nil {
return nil, 0, errors.New("tiff: failed to read IFD tag count: " + err.Error())
}
// load tags
for n := 0; n < int(nTags); n++ {
t, err := DecodeTag(r, order)
if err != nil {
return nil, 0, err
}
d.Tags = append(d.Tags, t)
}
// get offset to next ifd
err = binary.Read(r, order, &offset)
if err != nil {
return nil, 0, errors.New("tiff: falied to read offset to next IFD: " + err.Error())
}
return d, offset, nil
}
func (d *Dir) String() string {
s := "Dir{"
for _, t := range d.Tags {
s += t.String() + ", "
}
return s + "}"
}