summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff')
-rw-r--r--Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go438
-rw-r--r--Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff.go153
-rw-r--r--Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff_test.go235
3 files changed, 826 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go
new file mode 100644
index 000000000..66b68e334
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go
@@ -0,0 +1,438 @@
+package tiff
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// Format specifies the Go type equivalent used to represent the basic
+// tiff data types.
+type Format int
+
+const (
+ IntVal Format = iota
+ FloatVal
+ RatVal
+ StringVal
+ UndefVal
+ OtherVal
+)
+
+var ErrShortReadTagValue = errors.New("tiff: short read of tag value")
+
+var formatNames = map[Format]string{
+ IntVal: "int",
+ FloatVal: "float",
+ RatVal: "rational",
+ StringVal: "string",
+ UndefVal: "undefined",
+ OtherVal: "other",
+}
+
+// DataType represents the basic tiff tag data types.
+type DataType uint16
+
+const (
+ DTByte DataType = 1
+ DTAscii = 2
+ DTShort = 3
+ DTLong = 4
+ DTRational = 5
+ DTSByte = 6
+ DTUndefined = 7
+ DTSShort = 8
+ DTSLong = 9
+ DTSRational = 10
+ DTFloat = 11
+ DTDouble = 12
+)
+
+var typeNames = map[DataType]string{
+ DTByte: "byte",
+ DTAscii: "ascii",
+ DTShort: "short",
+ DTLong: "long",
+ DTRational: "rational",
+ DTSByte: "signed byte",
+ DTUndefined: "undefined",
+ DTSShort: "signed short",
+ DTSLong: "signed long",
+ DTSRational: "signed rational",
+ DTFloat: "float",
+ DTDouble: "double",
+}
+
+// typeSize specifies the size in bytes of each type.
+var typeSize = map[DataType]uint32{
+ DTByte: 1,
+ DTAscii: 1,
+ DTShort: 2,
+ DTLong: 4,
+ DTRational: 8,
+ DTSByte: 1,
+ DTUndefined: 1,
+ DTSShort: 2,
+ DTSLong: 4,
+ DTSRational: 8,
+ DTFloat: 4,
+ DTDouble: 8,
+}
+
+// Tag reflects the parsed content of a tiff IFD tag.
+type Tag struct {
+ // Id is the 2-byte tiff tag identifier.
+ Id uint16
+ // Type is an integer (1 through 12) indicating the tag value's data type.
+ Type DataType
+ // Count is the number of type Type stored in the tag's value (i.e. the
+ // tag's value is an array of type Type and length Count).
+ Count uint32
+ // Val holds the bytes that represent the tag's value.
+ Val []byte
+ // ValOffset holds byte offset of the tag value w.r.t. the beginning of the
+ // reader it was decoded from. Zero if the tag value fit inside the offset
+ // field.
+ ValOffset uint32
+
+ order binary.ByteOrder
+ intVals []int64
+ floatVals []float64
+ ratVals [][]int64
+ strVal string
+ format Format
+}
+
+// DecodeTag parses a tiff-encoded IFD tag from r and returns a Tag object. The
+// first read from r should be the first byte of the tag. ReadAt offsets should
+// generally be relative to the beginning of the tiff structure (not relative
+// to the beginning of the tag).
+func DecodeTag(r ReadAtReader, order binary.ByteOrder) (*Tag, error) {
+ t := new(Tag)
+ t.order = order
+
+ err := binary.Read(r, order, &t.Id)
+ if err != nil {
+ return nil, errors.New("tiff: tag id read failed: " + err.Error())
+ }
+
+ err = binary.Read(r, order, &t.Type)
+ if err != nil {
+ return nil, errors.New("tiff: tag type read failed: " + err.Error())
+ }
+
+ err = binary.Read(r, order, &t.Count)
+ if err != nil {
+ return nil, errors.New("tiff: tag component count read failed: " + err.Error())
+ }
+
+ // There seems to be a relatively common corrupt tag which has a Count of
+ // MaxUint32. This is probably not a valid value, so return early.
+ if t.Count == 1<<32-1 {
+ return t, errors.New("invalid Count offset in tag")
+ }
+
+ valLen := typeSize[t.Type] * t.Count
+ if valLen == 0 {
+ return t, errors.New("zero length tag value")
+ }
+
+ if valLen > 4 {
+ binary.Read(r, order, &t.ValOffset)
+
+ // Use a bytes.Buffer so we don't allocate a huge slice if the tag
+ // is corrupt.
+ var buff bytes.Buffer
+ sr := io.NewSectionReader(r, int64(t.ValOffset), int64(valLen))
+ n, err := io.Copy(&buff, sr)
+ if err != nil {
+ return t, errors.New("tiff: tag value read failed: " + err.Error())
+ } else if n != int64(valLen) {
+ return t, ErrShortReadTagValue
+ }
+ t.Val = buff.Bytes()
+
+ } else {
+ val := make([]byte, valLen)
+ if _, err = io.ReadFull(r, val); err != nil {
+ return t, errors.New("tiff: tag offset read failed: " + err.Error())
+ }
+ // ignore padding.
+ if _, err = io.ReadFull(r, make([]byte, 4-valLen)); err != nil {
+ return t, errors.New("tiff: tag offset read failed: " + err.Error())
+ }
+
+ t.Val = val
+ }
+
+ return t, t.convertVals()
+}
+
+func (t *Tag) convertVals() error {
+ r := bytes.NewReader(t.Val)
+
+ switch t.Type {
+ case DTAscii:
+ if len(t.Val) > 0 {
+ t.strVal = string(t.Val[:len(t.Val)-1]) // ignore the last byte (NULL).
+ }
+ case DTByte:
+ var v uint8
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTShort:
+ var v uint16
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTLong:
+ var v uint32
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTSByte:
+ var v int8
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTSShort:
+ var v int16
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTSLong:
+ var v int32
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTRational:
+ t.ratVals = make([][]int64, int(t.Count))
+ for i := range t.ratVals {
+ var n, d uint32
+ err := binary.Read(r, t.order, &n)
+ if err != nil {
+ return err
+ }
+ err = binary.Read(r, t.order, &d)
+ if err != nil {
+ return err
+ }
+ t.ratVals[i] = []int64{int64(n), int64(d)}
+ }
+ case DTSRational:
+ t.ratVals = make([][]int64, int(t.Count))
+ for i := range t.ratVals {
+ var n, d int32
+ err := binary.Read(r, t.order, &n)
+ if err != nil {
+ return err
+ }
+ err = binary.Read(r, t.order, &d)
+ if err != nil {
+ return err
+ }
+ t.ratVals[i] = []int64{int64(n), int64(d)}
+ }
+ case DTFloat: // float32
+ t.floatVals = make([]float64, int(t.Count))
+ for i := range t.floatVals {
+ var v float32
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.floatVals[i] = float64(v)
+ }
+ case DTDouble:
+ t.floatVals = make([]float64, int(t.Count))
+ for i := range t.floatVals {
+ var u float64
+ err := binary.Read(r, t.order, &u)
+ if err != nil {
+ return err
+ }
+ t.floatVals[i] = u
+ }
+ }
+
+ switch t.Type {
+ case DTByte, DTShort, DTLong, DTSByte, DTSShort, DTSLong:
+ t.format = IntVal
+ case DTRational, DTSRational:
+ t.format = RatVal
+ case DTFloat, DTDouble:
+ t.format = FloatVal
+ case DTAscii:
+ t.format = StringVal
+ case DTUndefined:
+ t.format = UndefVal
+ default:
+ t.format = OtherVal
+ }
+
+ return nil
+}
+
+// Format returns a value indicating which method can be called to retrieve the
+// tag's value properly typed (e.g. integer, rational, etc.).
+func (t *Tag) Format() Format { return t.format }
+
+func (t *Tag) typeErr(to Format) error {
+ return &wrongFmtErr{typeNames[t.Type], formatNames[to]}
+}
+
+// Rat returns the tag's i'th value as a rational number. It returns a nil and
+// an error if this tag's Format is not RatVal. It panics for zero deminators
+// or if i is out of range.
+func (t *Tag) Rat(i int) (*big.Rat, error) {
+ n, d, err := t.Rat2(i)
+ if err != nil {
+ return nil, err
+ }
+ return big.NewRat(n, d), nil
+}
+
+// Rat2 returns the tag's i'th value as a rational number represented by a
+// numerator-denominator pair. It returns an error if the tag's Format is not
+// RatVal. It panics if i is out of range.
+func (t *Tag) Rat2(i int) (num, den int64, err error) {
+ if t.format != RatVal {
+ return 0, 0, t.typeErr(RatVal)
+ }
+ return t.ratVals[i][0], t.ratVals[i][1], nil
+}
+
+// Int64 returns the tag's i'th value as an integer. It returns an error if the
+// tag's Format is not IntVal. It panics if i is out of range.
+func (t *Tag) Int64(i int) (int64, error) {
+ if t.format != IntVal {
+ return 0, t.typeErr(IntVal)
+ }
+ return t.intVals[i], nil
+}
+
+// Int returns the tag's i'th value as an integer. It returns an error if the
+// tag's Format is not IntVal. It panics if i is out of range.
+func (t *Tag) Int(i int) (int, error) {
+ if t.format != IntVal {
+ return 0, t.typeErr(IntVal)
+ }
+ return int(t.intVals[i]), nil
+}
+
+// Float returns the tag's i'th value as a float. It returns an error if the
+// tag's Format is not IntVal. It panics if i is out of range.
+func (t *Tag) Float(i int) (float64, error) {
+ if t.format != FloatVal {
+ return 0, t.typeErr(FloatVal)
+ }
+ return t.floatVals[i], nil
+}
+
+// StringVal returns the tag's value as a string. It returns an error if the
+// tag's Format is not StringVal. It panics if i is out of range.
+func (t *Tag) StringVal() (string, error) {
+ if t.format != StringVal {
+ return "", t.typeErr(StringVal)
+ }
+ return t.strVal, nil
+}
+
+// String returns a nicely formatted version of the tag.
+func (t *Tag) String() string {
+ data, err := t.MarshalJSON()
+ if err != nil {
+ return "ERROR: " + err.Error()
+ }
+
+ if t.Count == 1 {
+ return strings.Trim(fmt.Sprintf("%s", data), "[]")
+ }
+ return fmt.Sprintf("%s", data)
+}
+
+func (t *Tag) MarshalJSON() ([]byte, error) {
+ switch t.format {
+ case StringVal, UndefVal:
+ return nullString(t.Val), nil
+ case OtherVal:
+ return []byte(fmt.Sprintf("unknown tag type '%v'", t.Type)), nil
+ }
+
+ rv := []string{}
+ for i := 0; i < int(t.Count); i++ {
+ switch t.format {
+ case RatVal:
+ n, d, _ := t.Rat2(i)
+ rv = append(rv, fmt.Sprintf(`"%v/%v"`, n, d))
+ case FloatVal:
+ v, _ := t.Float(i)
+ rv = append(rv, fmt.Sprintf("%v", v))
+ case IntVal:
+ v, _ := t.Int(i)
+ rv = append(rv, fmt.Sprintf("%v", v))
+ }
+ }
+ return []byte(fmt.Sprintf(`[%s]`, strings.Join(rv, ","))), nil
+}
+
+func nullString(in []byte) []byte {
+ rv := bytes.Buffer{}
+ rv.WriteByte('"')
+ for _, b := range in {
+ if unicode.IsPrint(rune(b)) {
+ rv.WriteByte(b)
+ }
+ }
+ rv.WriteByte('"')
+ rvb := rv.Bytes()
+ if utf8.Valid(rvb) {
+ return rvb
+ }
+ return []byte(`""`)
+}
+
+type wrongFmtErr struct {
+ From, To string
+}
+
+func (e *wrongFmtErr) Error() string {
+ return fmt.Sprintf("cannot convert tag type '%v' into '%v'", e.From, e.To)
+}
diff --git a/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff.go b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff.go
new file mode 100644
index 000000000..771e91878
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff.go
@@ -0,0 +1,153 @@
+// 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 + "}"
+}
diff --git a/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff_test.go b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff_test.go
new file mode 100644
index 000000000..5db348dc8
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tiff_test.go
@@ -0,0 +1,235 @@
+package tiff
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/hex"
+ "flag"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+var dataDir = flag.String("test_data_dir", ".", "Directory where the data files for testing are located")
+
+type input struct {
+ tgId string
+ tpe string
+ nVals string
+ offset string
+ val string
+}
+
+type output struct {
+ id uint16
+ typ DataType
+ count uint32
+ val []byte
+}
+
+type tagTest struct {
+ big input // big endian
+ little input // little endian
+ out output
+}
+
+///////////////////////////////////////////////
+//// Big endian Tests /////////////////////////
+///////////////////////////////////////////////
+var set1 = []tagTest{
+ //////////// string type //////////////
+ tagTest{
+ // {"TgId", "TYPE", "N-VALUES", "OFFSET--", "VAL..."},
+ input{"0003", "0002", "00000002", "11000000", ""},
+ input{"0300", "0200", "02000000", "11000000", ""},
+ output{0x0003, DataType(0x0002), 0x0002, []byte{0x11, 0x00}},
+ },
+ tagTest{
+ input{"0001", "0002", "00000006", "00000012", "111213141516"},
+ input{"0100", "0200", "06000000", "12000000", "111213141516"},
+ output{0x0001, DataType(0x0002), 0x0006, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
+ },
+ //////////// int (1-byte) type ////////////////
+ tagTest{
+ input{"0001", "0001", "00000001", "11000000", ""},
+ input{"0100", "0100", "01000000", "11000000", ""},
+ output{0x0001, DataType(0x0001), 0x0001, []byte{0x11}},
+ },
+ tagTest{
+ input{"0001", "0001", "00000005", "00000010", "1112131415"},
+ input{"0100", "0100", "05000000", "10000000", "1112131415"},
+ output{0x0001, DataType(0x0001), 0x0005, []byte{0x11, 0x12, 0x13, 0x14, 0x15}},
+ },
+ tagTest{
+ input{"0001", "0006", "00000001", "11000000", ""},
+ input{"0100", "0600", "01000000", "11000000", ""},
+ output{0x0001, DataType(0x0006), 0x0001, []byte{0x11}},
+ },
+ tagTest{
+ input{"0001", "0006", "00000005", "00000010", "1112131415"},
+ input{"0100", "0600", "05000000", "10000000", "1112131415"},
+ output{0x0001, DataType(0x0006), 0x0005, []byte{0x11, 0x12, 0x13, 0x14, 0x15}},
+ },
+ //////////// int (2-byte) types ////////////////
+ tagTest{
+ input{"0001", "0003", "00000002", "11111212", ""},
+ input{"0100", "0300", "02000000", "11111212", ""},
+ output{0x0001, DataType(0x0003), 0x0002, []byte{0x11, 0x11, 0x12, 0x12}},
+ },
+ tagTest{
+ input{"0001", "0003", "00000003", "00000010", "111213141516"},
+ input{"0100", "0300", "03000000", "10000000", "111213141516"},
+ output{0x0001, DataType(0x0003), 0x0003, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
+ },
+ tagTest{
+ input{"0001", "0008", "00000001", "11120000", ""},
+ input{"0100", "0800", "01000000", "11120000", ""},
+ output{0x0001, DataType(0x0008), 0x0001, []byte{0x11, 0x12}},
+ },
+ tagTest{
+ input{"0001", "0008", "00000003", "00000100", "111213141516"},
+ input{"0100", "0800", "03000000", "00100000", "111213141516"},
+ output{0x0001, DataType(0x0008), 0x0003, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
+ },
+ //////////// int (4-byte) types ////////////////
+ tagTest{
+ input{"0001", "0004", "00000001", "11121314", ""},
+ input{"0100", "0400", "01000000", "11121314", ""},
+ output{0x0001, DataType(0x0004), 0x0001, []byte{0x11, 0x12, 0x13, 0x14}},
+ },
+ tagTest{
+ input{"0001", "0004", "00000002", "00000010", "1112131415161718"},
+ input{"0100", "0400", "02000000", "10000000", "1112131415161718"},
+ output{0x0001, DataType(0x0004), 0x0002, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}},
+ },
+ tagTest{
+ input{"0001", "0009", "00000001", "11121314", ""},
+ input{"0100", "0900", "01000000", "11121314", ""},
+ output{0x0001, DataType(0x0009), 0x0001, []byte{0x11, 0x12, 0x13, 0x14}},
+ },
+ tagTest{
+ input{"0001", "0009", "00000002", "00000011", "1112131415161819"},
+ input{"0100", "0900", "02000000", "11000000", "1112131415161819"},
+ output{0x0001, DataType(0x0009), 0x0002, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
+ },
+ //////////// rational types ////////////////////
+ tagTest{
+ input{"0001", "0005", "00000001", "00000010", "1112131415161718"},
+ input{"0100", "0500", "01000000", "10000000", "1112131415161718"},
+ output{0x0001, DataType(0x0005), 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}},
+ },
+ tagTest{
+ input{"0001", "000A", "00000001", "00000011", "1112131415161819"},
+ input{"0100", "0A00", "01000000", "11000000", "1112131415161819"},
+ output{0x0001, DataType(0x000A), 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
+ },
+ //////////// float types ///////////////////////
+ tagTest{
+ input{"0001", "0005", "00000001", "00000010", "1112131415161718"},
+ input{"0100", "0500", "01000000", "10000000", "1112131415161718"},
+ output{0x0001, DataType(0x0005), 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}},
+ },
+ tagTest{
+ input{"0101", "000A", "00000001", "00000011", "1112131415161819"},
+ input{"0101", "0A00", "01000000", "11000000", "1112131415161819"},
+ output{0x0101, DataType(0x000A), 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
+ },
+}
+
+func TestDecodeTag(t *testing.T) {
+ for i, tst := range set1 {
+ testSingle(t, binary.BigEndian, tst.big, tst.out, i)
+ testSingle(t, binary.LittleEndian, tst.little, tst.out, i)
+ }
+}
+
+func testSingle(t *testing.T, order binary.ByteOrder, in input, out output, i int) {
+ data := buildInput(in, order)
+ buf := bytes.NewReader(data)
+ tg, err := DecodeTag(buf, order)
+ if err != nil {
+ t.Errorf("(%v) tag %v%+v decode failed: %v", order, i, in, err)
+ return
+ }
+
+ if tg.Id != out.id {
+ t.Errorf("(%v) tag %v id decode: expected %v, got %v", order, i, out.id, tg.Id)
+ }
+ if tg.Type != out.typ {
+ t.Errorf("(%v) tag %v type decode: expected %v, got %v", order, i, out.typ, tg.Type)
+ }
+ if tg.Count != out.count {
+ t.Errorf("(%v) tag %v component count decode: expected %v, got %v", order, i, out.count, tg.Count)
+ }
+ if !bytes.Equal(tg.Val, out.val) {
+ t.Errorf("(%v) tag %v value decode: expected %v, got %v", order, i, out.val, tg.Val)
+ }
+}
+
+// buildInputBig creates a byte-slice based on big-endian ordered input
+func buildInput(in input, order binary.ByteOrder) []byte {
+ data := make([]byte, 0)
+ d, _ := hex.DecodeString(in.tgId)
+ data = append(data, d...)
+ d, _ = hex.DecodeString(in.tpe)
+ data = append(data, d...)
+ d, _ = hex.DecodeString(in.nVals)
+ data = append(data, d...)
+ d, _ = hex.DecodeString(in.offset)
+ data = append(data, d...)
+
+ if in.val != "" {
+ off := order.Uint32(d)
+ for i := 0; i < int(off)-12; i++ {
+ data = append(data, 0xFF)
+ }
+
+ d, _ = hex.DecodeString(in.val)
+ data = append(data, d...)
+ }
+
+ return data
+}
+
+func TestDecode(t *testing.T) {
+ name := filepath.Join(*dataDir, "sample1.tif")
+ f, err := os.Open(name)
+ if err != nil {
+ t.Fatalf("%v\n", err)
+ }
+
+ tif, err := Decode(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Log(tif)
+}
+
+func TestDecodeTag_blob(t *testing.T) {
+ buf := bytes.NewReader(data())
+ buf.Seek(10, 1)
+ tg, err := DecodeTag(buf, binary.LittleEndian)
+ if err != nil {
+ t.Fatalf("tag decode failed: %v", err)
+ }
+
+ t.Logf("tag: %v+\n", tg)
+ n, d, err := tg.Rat2(0)
+ if err != nil {
+ t.Fatalf("tag decoded wrong type: %v", err)
+ }
+ t.Logf("tag rat val: %v/%v\n", n, d)
+}
+
+func data() []byte {
+ s1 := "49492A000800000002001A0105000100"
+ s1 += "00002600000069870400010000001102"
+ s1 += "0000000000004800000001000000"
+
+ dat, err := hex.DecodeString(s1)
+ if err != nil {
+ panic("invalid string fixture")
+ }
+ return dat
+}