tiff.go (3741B)
1 // Package tiff implements TIFF decoding as defined in TIFF 6.0 specification at 2 // http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf 3 package tiff 4 5 import ( 6 "bytes" 7 "encoding/binary" 8 "errors" 9 "fmt" 10 "io" 11 "io/ioutil" 12 ) 13 14 // ReadAtReader is used when decoding Tiff tags and directories 15 type ReadAtReader interface { 16 io.Reader 17 io.ReaderAt 18 } 19 20 // Tiff provides access to a decoded tiff data structure. 21 type Tiff struct { 22 // Dirs is an ordered slice of the tiff's Image File Directories (IFDs). 23 // The IFD at index 0 is IFD0. 24 Dirs []*Dir 25 // The tiff's byte-encoding (i.e. big/little endian). 26 Order binary.ByteOrder 27 } 28 29 // Decode parses tiff-encoded data from r and returns a Tiff struct that 30 // reflects the structure and content of the tiff data. The first read from r 31 // should be the first byte of the tiff-encoded data and not necessarily the 32 // first byte of an os.File object. 33 func Decode(r io.Reader) (*Tiff, error) { 34 data, err := ioutil.ReadAll(r) 35 if err != nil { 36 return nil, errors.New("tiff: could not read data") 37 } 38 buf := bytes.NewReader(data) 39 40 t := new(Tiff) 41 42 // read byte order 43 bo := make([]byte, 2) 44 if _, err = io.ReadFull(buf, bo); err != nil { 45 return nil, errors.New("tiff: could not read tiff byte order") 46 } 47 if string(bo) == "II" { 48 t.Order = binary.LittleEndian 49 } else if string(bo) == "MM" { 50 t.Order = binary.BigEndian 51 } else { 52 return nil, errors.New("tiff: could not read tiff byte order") 53 } 54 55 // check for special tiff marker 56 var sp int16 57 err = binary.Read(buf, t.Order, &sp) 58 if err != nil || 42 != sp { 59 return nil, errors.New("tiff: could not find special tiff marker") 60 } 61 62 // load offset to first IFD 63 var offset int32 64 err = binary.Read(buf, t.Order, &offset) 65 if err != nil { 66 return nil, errors.New("tiff: could not read offset to first IFD") 67 } 68 69 // load IFD's 70 var d *Dir 71 prev := offset 72 for offset != 0 { 73 // seek to offset 74 _, err := buf.Seek(int64(offset), 0) 75 if err != nil { 76 return nil, errors.New("tiff: seek to IFD failed") 77 } 78 79 if buf.Len() == 0 { 80 return nil, errors.New("tiff: seek offset after EOF") 81 } 82 83 // load the dir 84 d, offset, err = DecodeDir(buf, t.Order) 85 if err != nil { 86 return nil, err 87 } 88 89 if offset == prev { 90 return nil, errors.New("tiff: recursive IFD") 91 } 92 prev = offset 93 94 t.Dirs = append(t.Dirs, d) 95 } 96 97 return t, nil 98 } 99 100 func (tf *Tiff) String() string { 101 var buf bytes.Buffer 102 fmt.Fprint(&buf, "Tiff{") 103 for _, d := range tf.Dirs { 104 fmt.Fprintf(&buf, "%s, ", d.String()) 105 } 106 fmt.Fprintf(&buf, "}") 107 return buf.String() 108 } 109 110 // Dir provides access to the parsed content of a tiff Image File Directory (IFD). 111 type Dir struct { 112 Tags []*Tag 113 } 114 115 // DecodeDir parses a tiff-encoded IFD from r and returns a Dir object. offset 116 // is the offset to the next IFD. The first read from r should be at the first 117 // byte of the IFD. ReadAt offsets should generally be relative to the 118 // beginning of the tiff structure (not relative to the beginning of the IFD). 119 func DecodeDir(r ReadAtReader, order binary.ByteOrder) (d *Dir, offset int32, err error) { 120 d = new(Dir) 121 122 // get num of tags in ifd 123 var nTags int16 124 err = binary.Read(r, order, &nTags) 125 if err != nil { 126 return nil, 0, errors.New("tiff: failed to read IFD tag count: " + err.Error()) 127 } 128 129 // load tags 130 for n := 0; n < int(nTags); n++ { 131 t, err := DecodeTag(r, order) 132 if err != nil { 133 return nil, 0, err 134 } 135 d.Tags = append(d.Tags, t) 136 } 137 138 // get offset to next ifd 139 err = binary.Read(r, order, &offset) 140 if err != nil { 141 return nil, 0, errors.New("tiff: falied to read offset to next IFD: " + err.Error()) 142 } 143 144 return d, offset, nil 145 } 146 147 func (d *Dir) String() string { 148 s := "Dir{" 149 for _, t := range d.Tags { 150 s += t.String() + ", " 151 } 152 return s + "}" 153 }