diff options
Diffstat (limited to 'Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go')
-rw-r--r-- | Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go | 438 |
1 files changed, 0 insertions, 438 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 deleted file mode 100644 index 66b68e334..000000000 --- a/Godeps/_workspace/src/github.com/rwcarlsen/goexif/tiff/tag.go +++ /dev/null @@ -1,438 +0,0 @@ -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) -} |