src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }