created type+constants for raw tiff tag data types. parametrized sample test file dirs. minor tweaks.
This commit is contained in:
+16
-4
@@ -1,6 +1,7 @@
|
||||
package exif
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -14,6 +15,8 @@ import (
|
||||
// switch to true to regenerate regression expected values
|
||||
var regenRegress = false
|
||||
|
||||
var dataDir = flag.String("test_data_dir", ".", "Directory where the data files for testing are located")
|
||||
|
||||
// TestRegenRegress regenerates the expected image exif fields/values for
|
||||
// sample images.
|
||||
func TestRegenRegress(t *testing.T) {
|
||||
@@ -81,18 +84,23 @@ func (w *regresswalk) Walk(name FieldName, tag *tiff.Tag) error {
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
fpath := "samples"
|
||||
fpath := filepath.Join(*dataDir, "samples")
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not open sample directory: %v", err)
|
||||
t.Fatalf("Could not open sample directory '%s': %v", fpath, err)
|
||||
}
|
||||
|
||||
names, err := f.Readdirnames(0)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not read sample directory: %v", err)
|
||||
t.Fatalf("Could not read sample directory '%s': %v", fpath, err)
|
||||
}
|
||||
|
||||
cnt := 0
|
||||
for _, name := range names {
|
||||
if !strings.HasSuffix(name, ".jpg") {
|
||||
t.Logf("skipping non .jpg file %v", name)
|
||||
continue
|
||||
}
|
||||
t.Logf("testing file %v", name)
|
||||
f, err := os.Open(filepath.Join(fpath, name))
|
||||
if err != nil {
|
||||
@@ -107,6 +115,10 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
|
||||
x.Walk(&walker{name, t})
|
||||
cnt++
|
||||
}
|
||||
if cnt != len(regressExpected) {
|
||||
t.Errorf("Did not process enough samples, got %d, want %d", cnt, len(regressExpected))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +136,7 @@ func (w *walker) Walk(field FieldName, tag *tiff.Tag) error {
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
name := "sample1.jpg"
|
||||
name := filepath.Join(*dataDir, "sample1.jpg")
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
|
||||
+61
-43
@@ -12,10 +12,10 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// TypeCategory specifies the category of a type.
|
||||
// TypeCategory specifies the Go type equivalent used to represent the basic
|
||||
// tiff data types.
|
||||
type TypeCategory int
|
||||
|
||||
// Type categories.
|
||||
const (
|
||||
IntVal TypeCategory = iota
|
||||
FloatVal
|
||||
@@ -25,28 +25,46 @@ const (
|
||||
OtherVal
|
||||
)
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// typeSize specifies the size in bytes of each type.
|
||||
var typeSize = map[uint16]uint32{
|
||||
1: 1,
|
||||
2: 1,
|
||||
3: 2,
|
||||
4: 4,
|
||||
5: 8,
|
||||
6: 1,
|
||||
7: 1,
|
||||
8: 2,
|
||||
9: 4,
|
||||
10: 8,
|
||||
11: 4,
|
||||
12: 8,
|
||||
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 format.
|
||||
Type 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
|
||||
@@ -118,11 +136,11 @@ func (t *Tag) convertVals() {
|
||||
r := bytes.NewReader(t.Val)
|
||||
|
||||
switch t.Type {
|
||||
case 2: // ascii string
|
||||
case DTAscii:
|
||||
if len(t.Val) > 0 {
|
||||
t.strVal = string(t.Val[:len(t.Val)-1]) // ignore the last byte (NULL).
|
||||
}
|
||||
case 1:
|
||||
case DTByte:
|
||||
var v uint8
|
||||
t.intVals = make([]int64, int(t.Count))
|
||||
for i := range t.intVals {
|
||||
@@ -130,7 +148,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.intVals[i] = int64(v)
|
||||
}
|
||||
case 3:
|
||||
case DTShort:
|
||||
var v uint16
|
||||
t.intVals = make([]int64, int(t.Count))
|
||||
for i := range t.intVals {
|
||||
@@ -138,7 +156,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.intVals[i] = int64(v)
|
||||
}
|
||||
case 4:
|
||||
case DTLong:
|
||||
var v uint32
|
||||
t.intVals = make([]int64, int(t.Count))
|
||||
for i := range t.intVals {
|
||||
@@ -146,7 +164,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.intVals[i] = int64(v)
|
||||
}
|
||||
case 6:
|
||||
case DTSByte:
|
||||
var v int8
|
||||
t.intVals = make([]int64, int(t.Count))
|
||||
for i := range t.intVals {
|
||||
@@ -154,7 +172,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.intVals[i] = int64(v)
|
||||
}
|
||||
case 8:
|
||||
case DTSShort:
|
||||
var v int16
|
||||
t.intVals = make([]int64, int(t.Count))
|
||||
for i := range t.intVals {
|
||||
@@ -162,7 +180,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.intVals[i] = int64(v)
|
||||
}
|
||||
case 9:
|
||||
case DTSLong:
|
||||
var v int32
|
||||
t.intVals = make([]int64, int(t.Count))
|
||||
for i := range t.intVals {
|
||||
@@ -170,7 +188,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.intVals[i] = int64(v)
|
||||
}
|
||||
case 5: // unsigned rational
|
||||
case DTRational:
|
||||
t.ratVals = make([][]int64, int(t.Count))
|
||||
for i := range t.ratVals {
|
||||
var n, d uint32
|
||||
@@ -180,7 +198,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.ratVals[i] = []int64{int64(n), int64(d)}
|
||||
}
|
||||
case 10: // signed rational
|
||||
case DTSRational:
|
||||
t.ratVals = make([][]int64, int(t.Count))
|
||||
for i := range t.ratVals {
|
||||
var n, d int32
|
||||
@@ -190,7 +208,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.ratVals[i] = []int64{int64(n), int64(d)}
|
||||
}
|
||||
case 11: // float32
|
||||
case DTFloat: // float32
|
||||
t.floatVals = make([]float64, int(t.Count))
|
||||
for i := range t.floatVals {
|
||||
var v float32
|
||||
@@ -198,7 +216,7 @@ func (t *Tag) convertVals() {
|
||||
panicOn(err)
|
||||
t.floatVals[i] = float64(v)
|
||||
}
|
||||
case 12: // float64 (double)
|
||||
case DTDouble:
|
||||
t.floatVals = make([]float64, int(t.Count))
|
||||
for i := range t.floatVals {
|
||||
var u float64
|
||||
@@ -209,34 +227,34 @@ func (t *Tag) convertVals() {
|
||||
}
|
||||
}
|
||||
|
||||
// Format returns a value indicating which method can be called to retrieve the
|
||||
// TypeCategory 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) TypeCategory() TypeCategory {
|
||||
switch t.Type {
|
||||
case 1, 3, 4, 6, 8, 9:
|
||||
case DTByte, DTShort, DTLong, DTSByte, DTSShort, DTSLong:
|
||||
return IntVal
|
||||
case 5, 10:
|
||||
case DTRational, DTSRational:
|
||||
return RatVal
|
||||
case 11, 12:
|
||||
case DTFloat, DTDouble:
|
||||
return FloatVal
|
||||
case 2:
|
||||
case DTAscii:
|
||||
return StringVal
|
||||
case 7:
|
||||
case DTUndefined:
|
||||
return UndefVal
|
||||
}
|
||||
return OtherVal
|
||||
}
|
||||
|
||||
// Rat returns the tag's i'th value as a rational number. It panics if the tag format
|
||||
// is not RatVal, if the denominator is zero, or if the tag has no i'th
|
||||
// component. If a denominator could be zero, use Rat2.
|
||||
// Rat returns the tag's i'th value as a rational number. It panics if the tag
|
||||
// TypeCategory is not RatVal, if the denominator is zero, or if the tag has no
|
||||
// i'th component. If a denominator could be zero, use Rat2.
|
||||
func (t *Tag) Rat(i int) *big.Rat {
|
||||
n, d := t.Rat2(i)
|
||||
return big.NewRat(n, d)
|
||||
}
|
||||
|
||||
// Rat2 returns the tag's i'th value as a rational number represented by a
|
||||
// numerator-denominator pair. It panics if the tag format is not RatVal
|
||||
// numerator-denominator pair. It panics if the tag TypeCategory is not RatVal
|
||||
// or if the tag value has no i'th component.
|
||||
func (t *Tag) Rat2(i int) (num, den int64) {
|
||||
if t.TypeCategory() != RatVal {
|
||||
@@ -245,8 +263,8 @@ func (t *Tag) Rat2(i int) (num, den int64) {
|
||||
return t.ratVals[i][0], t.ratVals[i][1]
|
||||
}
|
||||
|
||||
// Int returns the tag's i'th value as an integer. It panics if the tag format is not
|
||||
// IntVal or if the tag value has no i'th component.
|
||||
// Int returns the tag's i'th value as an integer. It panics if the tag
|
||||
// TypeCategory is not IntVal or if the tag value has no i'th component.
|
||||
func (t *Tag) Int(i int) int64 {
|
||||
if t.TypeCategory() != IntVal {
|
||||
panic("Tag type category is not 'int'")
|
||||
@@ -254,8 +272,8 @@ func (t *Tag) Int(i int) int64 {
|
||||
return t.intVals[i]
|
||||
}
|
||||
|
||||
// Float returns the tag's i'th value as a float. It panics if the tag format is not
|
||||
// FloatVal or if the tag value has no i'th component.
|
||||
// Float returns the tag's i'th value as a float. It panics if the tag
|
||||
// TypeCategory is not FloatVal or if the tag value has no i'th component.
|
||||
func (t *Tag) Float(i int) float64 {
|
||||
if t.TypeCategory() != FloatVal {
|
||||
panic("Tag type category is not 'float'")
|
||||
@@ -264,7 +282,7 @@ func (t *Tag) Float(i int) float64 {
|
||||
}
|
||||
|
||||
// StringVal returns the tag's value as a string. It panics if the tag
|
||||
// format is not StringVal
|
||||
// TypeCategory is not StringVal.
|
||||
func (t *Tag) StringVal() string {
|
||||
if t.TypeCategory() != StringVal {
|
||||
panic("Tag type category is not 'ascii string'")
|
||||
|
||||
+25
-21
@@ -4,10 +4,14 @@ 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
|
||||
@@ -18,7 +22,7 @@ type input struct {
|
||||
|
||||
type output struct {
|
||||
id uint16
|
||||
typ uint16
|
||||
typ DataType
|
||||
count uint32
|
||||
val []byte
|
||||
}
|
||||
@@ -38,97 +42,97 @@ var set1 = []tagTest{
|
||||
// {"TgId", "TYPE", "N-VALUES", "OFFSET--", "VAL..."},
|
||||
input{"0003", "0002", "00000002", "11000000", ""},
|
||||
input{"0300", "0200", "02000000", "11000000", ""},
|
||||
output{0x0003, 0x0002, 0x0002, []byte{0x11, 0x00}},
|
||||
output{0x0003, DataType(0x0002), 0x0002, []byte{0x11, 0x00}},
|
||||
},
|
||||
tagTest{
|
||||
input{"0001", "0002", "00000006", "00000012", "111213141516"},
|
||||
input{"0100", "0200", "06000000", "12000000", "111213141516"},
|
||||
output{0x0001, 0x0002, 0x0006, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
|
||||
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, 0x0001, 0x0001, []byte{0x11}},
|
||||
output{0x0001, DataType(0x0001), 0x0001, []byte{0x11}},
|
||||
},
|
||||
tagTest{
|
||||
input{"0001", "0001", "00000005", "00000010", "1112131415"},
|
||||
input{"0100", "0100", "05000000", "10000000", "1112131415"},
|
||||
output{0x0001, 0x0001, 0x0005, []byte{0x11, 0x12, 0x13, 0x14, 0x15}},
|
||||
output{0x0001, DataType(0x0001), 0x0005, []byte{0x11, 0x12, 0x13, 0x14, 0x15}},
|
||||
},
|
||||
tagTest{
|
||||
input{"0001", "0006", "00000001", "11000000", ""},
|
||||
input{"0100", "0600", "01000000", "11000000", ""},
|
||||
output{0x0001, 0x0006, 0x0001, []byte{0x11}},
|
||||
output{0x0001, DataType(0x0006), 0x0001, []byte{0x11}},
|
||||
},
|
||||
tagTest{
|
||||
input{"0001", "0006", "00000005", "00000010", "1112131415"},
|
||||
input{"0100", "0600", "05000000", "10000000", "1112131415"},
|
||||
output{0x0001, 0x0006, 0x0005, []byte{0x11, 0x12, 0x13, 0x14, 0x15}},
|
||||
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, 0x0003, 0x0002, []byte{0x11, 0x11, 0x12, 0x12}},
|
||||
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, 0x0003, 0x0003, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
|
||||
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, 0x0008, 0x0001, []byte{0x11, 0x12}},
|
||||
output{0x0001, DataType(0x0008), 0x0001, []byte{0x11, 0x12}},
|
||||
},
|
||||
tagTest{
|
||||
input{"0001", "0008", "00000003", "00000100", "111213141516"},
|
||||
input{"0100", "0800", "03000000", "00100000", "111213141516"},
|
||||
output{0x0001, 0x0008, 0x0003, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16}},
|
||||
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, 0x0004, 0x0001, []byte{0x11, 0x12, 0x13, 0x14}},
|
||||
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, 0x0004, 0x0002, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}},
|
||||
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, 0x0009, 0x0001, []byte{0x11, 0x12, 0x13, 0x14}},
|
||||
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, 0x0009, 0x0002, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
|
||||
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, 0x0005, 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}},
|
||||
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, 0x000A, 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
|
||||
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, 0x0005, 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}},
|
||||
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, 0x000A, 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
|
||||
output{0x0101, DataType(0x000A), 0x0001, []byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19}},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -188,7 +192,7 @@ func buildInput(in input, order binary.ByteOrder) []byte {
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
name := "sample1.tif"
|
||||
name := filepath.Join(*dataDir, "sample1.tif")
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
t.Fatalf("%v\n", err)
|
||||
@@ -212,7 +216,7 @@ func TestDecodeTag_blob(t *testing.T) {
|
||||
|
||||
t.Logf("tag: %v+\n", tg)
|
||||
n, d := tg.Rat2(0)
|
||||
t.Logf("tag rat val: %v\n", n, d)
|
||||
t.Logf("tag rat val: %v/%v\n", n, d)
|
||||
}
|
||||
|
||||
func data() []byte {
|
||||
|
||||
Reference in New Issue
Block a user