diff --git a/common_test.go b/common_test.go new file mode 100644 index 0000000..c29cb34 --- /dev/null +++ b/common_test.go @@ -0,0 +1,32 @@ +package exif + +import ( + "os" + "path" + + "io/ioutil" + + "github.com/dsoprea/go-logging" +) + +var ( + assetsPath = "" + testExifData = make([]byte, 0) +) + +func init() { + goPath := os.Getenv("GOPATH") + if goPath == "" { + log.Panicf("GOPATH is empty") + } + + assetsPath = path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets") + + // Load test EXIF data. + + filepath := path.Join(assetsPath, "NDM_8901.jpg.exif") + + var err error + testExifData, err = ioutil.ReadFile(filepath) + log.PanicIf(err) +} diff --git a/exif.go b/exif.go index 771de15..23e1f6c 100644 --- a/exif.go +++ b/exif.go @@ -1,128 +1,129 @@ package exif import ( - "os" - "errors" - "bytes" - "fmt" + "bytes" + "errors" + "fmt" + "os" - "io/ioutil" - "encoding/binary" + "encoding/binary" + "io/ioutil" - "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-logging" ) const ( - // ExifAddressableAreaStart is the absolute offset in the file that all - // offsets are relative to. - ExifAddressableAreaStart = uint32(0x0) + // ExifAddressableAreaStart is the absolute offset in the file that all + // offsets are relative to. + ExifAddressableAreaStart = uint32(0x0) - // ExifDefaultFirstIfdOffset is essentially the number of bytes in addition - // to `ExifAddressableAreaStart` that you have to move in order to escape - // the rest of the header and get to the earliest point where we can put - // stuff (which has to be the first IFD). This is the size of the header - // sequence containing the two-character byte-order, two-character fixed- - // bytes, and the four bytes describing the first-IFD offset. - ExifDefaultFirstIfdOffset = uint32(2 + 2 + 4) + // ExifDefaultFirstIfdOffset is essentially the number of bytes in addition + // to `ExifAddressableAreaStart` that you have to move in order to escape + // the rest of the header and get to the earliest point where we can put + // stuff (which has to be the first IFD). This is the size of the header + // sequence containing the two-character byte-order, two-character fixed- + // bytes, and the four bytes describing the first-IFD offset. + ExifDefaultFirstIfdOffset = uint32(2 + 2 + 4) ) var ( - exifLogger = log.NewLogger("exif.exif") + exifLogger = log.NewLogger("exif.exif") - // EncodeDefaultByteOrder is the default byte-order for encoding operations. - EncodeDefaultByteOrder = binary.BigEndian + // EncodeDefaultByteOrder is the default byte-order for encoding operations. + EncodeDefaultByteOrder = binary.BigEndian - BigEndianBoBytes = [2]byte { 'M', 'M' } - LittleEndianBoBytes = [2]byte { 'I', 'I' } + // Default byte order for tests. + TestDefaultByteOrder = binary.BigEndian - ByteOrderLookup = map[[2]byte]binary.ByteOrder { - BigEndianBoBytes: binary.BigEndian, - LittleEndianBoBytes: binary.LittleEndian, - } + BigEndianBoBytes = [2]byte{'M', 'M'} + LittleEndianBoBytes = [2]byte{'I', 'I'} - ByteOrderLookupR = map[binary.ByteOrder][2]byte { - binary.BigEndian: BigEndianBoBytes, - binary.LittleEndian: LittleEndianBoBytes, - } + ByteOrderLookup = map[[2]byte]binary.ByteOrder{ + BigEndianBoBytes: binary.BigEndian, + LittleEndianBoBytes: binary.LittleEndian, + } - ExifFixedBytesLookup = map[binary.ByteOrder][2]byte { - binary.LittleEndian: [2]byte { 0x2a, 0x00 }, - binary.BigEndian: [2]byte { 0x00, 0x2a }, - } + ByteOrderLookupR = map[binary.ByteOrder][2]byte{ + binary.BigEndian: BigEndianBoBytes, + binary.LittleEndian: LittleEndianBoBytes, + } + + ExifFixedBytesLookup = map[binary.ByteOrder][2]byte{ + binary.LittleEndian: [2]byte{0x2a, 0x00}, + binary.BigEndian: [2]byte{0x00, 0x2a}, + } ) var ( - ErrNoExif = errors.New("no exif data") - ErrExifHeaderError = errors.New("exif header error") + ErrNoExif = errors.New("no exif data") + ErrExifHeaderError = errors.New("exif header error") ) - // SearchAndExtractExif returns a slice from the beginning of the EXIF data the // end of the file (it's not practical to try and calculate where the data // actually ends). func SearchAndExtractExif(data []byte) (rawExif []byte, err error) { - defer func() { - if state := recover(); state != nil { - err := log.Wrap(state.(error)) - log.Panic(err) - } - }() + defer func() { + if state := recover(); state != nil { + err := log.Wrap(state.(error)) + log.Panic(err) + } + }() - // Search for the beginning of the EXIF information. The EXIF is near the - // beginning of our/most JPEGs, so this has a very low cost. + // Search for the beginning of the EXIF information. The EXIF is near the + // beginning of our/most JPEGs, so this has a very low cost. - foundAt := -1 - for i := 0; i < len(data); i++ { - if _, err := ParseExifHeader(data[i:]); err == nil { - foundAt = i - break - } else if log.Is(err, ErrNoExif) == false { - log.Panic(err) - } - } + foundAt := -1 + for i := 0; i < len(data); i++ { + if _, err := ParseExifHeader(data[i:]); err == nil { + foundAt = i + break + } else if log.Is(err, ErrNoExif) == false { + log.Panic(err) + } + } - if foundAt == -1 { - log.Panic(ErrNoExif) - } + if foundAt == -1 { + log.Panic(ErrNoExif) + } - return data[foundAt:], nil + return data[foundAt:], nil } // SearchFileAndExtractExif returns a slice from the beginning of the EXIF data // to the end of the file (it's not practical to try and calculate where the // data actually ends). func SearchFileAndExtractExif(filepath string) (rawExif []byte, err error) { - defer func() { - if state := recover(); state != nil { - err := log.Wrap(state.(error)) - log.Panic(err) - } - }() + defer func() { + if state := recover(); state != nil { + err := log.Wrap(state.(error)) + log.Panic(err) + } + }() - // Open the file. + // Open the file. - f, err := os.Open(filepath) - log.PanicIf(err) + f, err := os.Open(filepath) + log.PanicIf(err) - defer f.Close() + defer f.Close() - data, err := ioutil.ReadAll(f) - log.PanicIf(err) + data, err := ioutil.ReadAll(f) + log.PanicIf(err) - rawExif, err = SearchAndExtractExif(data) - log.PanicIf(err) + rawExif, err = SearchAndExtractExif(data) + log.PanicIf(err) - return rawExif, nil + return rawExif, nil } - type ExifHeader struct { - ByteOrder binary.ByteOrder - FirstIfdOffset uint32 + ByteOrder binary.ByteOrder + FirstIfdOffset uint32 } func (eh ExifHeader) String() string { - return fmt.Sprintf("ExifHeader<BYTE-ORDER=[%v] FIRST-IFD-OFFSET=(0x%02x)>", eh.ByteOrder, eh.FirstIfdOffset) + return fmt.Sprintf("ExifHeader<BYTE-ORDER=[%v] FIRST-IFD-OFFSET=(0x%02x)>", eh.ByteOrder, eh.FirstIfdOffset) } // ParseExifHeader parses the bytes at the very top of the header. @@ -130,102 +131,102 @@ func (eh ExifHeader) String() string { // This will panic with ErrNoExif on any data errors so that we can double as // an EXIF-detection routine. func ParseExifHeader(data []byte) (eh ExifHeader, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - // Good reference: - // - // CIPA DC-008-2016; JEITA CP-3451D - // -> http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf + // Good reference: + // + // CIPA DC-008-2016; JEITA CP-3451D + // -> http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf - byteOrderBytes := [2]byte { data[0], data[1] } + byteOrderBytes := [2]byte{data[0], data[1]} - byteOrder, found := ByteOrderLookup[byteOrderBytes] - if found == false { - exifLogger.Warningf(nil, "EXIF byte-order not recognized: [%v]", byteOrderBytes) - log.Panic(ErrNoExif) - } + byteOrder, found := ByteOrderLookup[byteOrderBytes] + if found == false { + exifLogger.Warningf(nil, "EXIF byte-order not recognized: [%v]", byteOrderBytes) + log.Panic(ErrNoExif) + } - fixedBytes := [2]byte { data[2], data[3] } - expectedFixedBytes := ExifFixedBytesLookup[byteOrder] - if fixedBytes != expectedFixedBytes { - exifLogger.Warningf(nil, "EXIF header fixed-bytes should be [%v] but are: [%v]", expectedFixedBytes, fixedBytes) - log.Panic(ErrNoExif) - } + fixedBytes := [2]byte{data[2], data[3]} + expectedFixedBytes := ExifFixedBytesLookup[byteOrder] + if fixedBytes != expectedFixedBytes { + exifLogger.Warningf(nil, "EXIF header fixed-bytes should be [%v] but are: [%v]", expectedFixedBytes, fixedBytes) + log.Panic(ErrNoExif) + } - firstIfdOffset := byteOrder.Uint32(data[4:8]) + firstIfdOffset := byteOrder.Uint32(data[4:8]) - eh = ExifHeader{ - ByteOrder: byteOrder, - FirstIfdOffset: firstIfdOffset, - } + eh = ExifHeader{ + ByteOrder: byteOrder, + FirstIfdOffset: firstIfdOffset, + } - return eh, nil + return eh, nil } // Visit recursively invokes a callback for every tag. func Visit(exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - eh, err = ParseExifHeader(exifData) - log.PanicIf(err) + eh, err = ParseExifHeader(exifData) + log.PanicIf(err) - ie := NewIfdEnumerate(exifData, eh.ByteOrder) + ie := NewIfdEnumerate(exifData, eh.ByteOrder) - err = ie.Scan(eh.FirstIfdOffset, visitor, true) - log.PanicIf(err) + err = ie.Scan(eh.FirstIfdOffset, visitor, true) + log.PanicIf(err) - return eh, nil + return eh, nil } // Collect recursively builds a static structure of all IFDs and tags. func Collect(exifData []byte) (eh ExifHeader, index IfdIndex, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - eh, err = ParseExifHeader(exifData) - log.PanicIf(err) + eh, err = ParseExifHeader(exifData) + log.PanicIf(err) - ie := NewIfdEnumerate(exifData, eh.ByteOrder) + ie := NewIfdEnumerate(exifData, eh.ByteOrder) - index, err = ie.Collect(eh.FirstIfdOffset, true) - log.PanicIf(err) + index, err = ie.Collect(eh.FirstIfdOffset, true) + log.PanicIf(err) - return eh, index, nil + return eh, index, nil } // BuildExifHeader constructs the bytes that go in the very beginning. func BuildExifHeader(byteOrder binary.ByteOrder, firstIfdOffset uint32) (headerBytes []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - b := new(bytes.Buffer) + b := new(bytes.Buffer) - // This is the point in the data that all offsets are relative to. - boBytes := ByteOrderLookupR[byteOrder] - _, err = b.WriteString(string(boBytes[:])) - log.PanicIf(err) + // This is the point in the data that all offsets are relative to. + boBytes := ByteOrderLookupR[byteOrder] + _, err = b.WriteString(string(boBytes[:])) + log.PanicIf(err) - fixedBytes := ExifFixedBytesLookup[byteOrder] + fixedBytes := ExifFixedBytesLookup[byteOrder] - _, err = b.Write(fixedBytes[:]) - log.PanicIf(err) + _, err = b.Write(fixedBytes[:]) + log.PanicIf(err) - err = binary.Write(b, byteOrder, firstIfdOffset) - log.PanicIf(err) + err = binary.Write(b, byteOrder, firstIfdOffset) + log.PanicIf(err) - return b.Bytes(), nil + return b.Bytes(), nil } diff --git a/tags.go b/tags.go index bc46c72..2689948 100644 --- a/tags.go +++ b/tags.go @@ -1,242 +1,231 @@ package exif import ( - "os" - "path" - "fmt" + "fmt" - "gopkg.in/yaml.v2" - "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-logging" + "gopkg.in/yaml.v2" ) const ( - // IFD1 + // IFD1 - ThumbnailOffsetTagId = 0x0201 - ThumbnailSizeTagId = 0x0202 + ThumbnailOffsetTagId = 0x0201 + ThumbnailSizeTagId = 0x0202 - // Exif + // Exif - TagVersionId = 0x0000 + TagVersionId = 0x0000 - TagLatitudeId = 0x0002 - TagLatitudeRefId = 0x0001 - TagLongitudeId = 0x0004 - TagLongitudeRefId = 0x0003 + TagLatitudeId = 0x0002 + TagLatitudeRefId = 0x0001 + TagLongitudeId = 0x0004 + TagLongitudeRefId = 0x0003 - TagTimestampId = 0x0007 - TagDatestampId = 0x001d + TagTimestampId = 0x0007 + TagDatestampId = 0x001d - TagAltitudeId = 0x0006 - TagAltitudeRefId = 0x0005 + TagAltitudeId = 0x0006 + TagAltitudeRefId = 0x0005 ) var ( - tagDataFilepath = "" + // tagsWithoutAlignment is a tag-lookup for tags whose value size won't + // necessarily be a multiple of its tag-type. + tagsWithoutAlignment = map[uint16]struct{}{ + // The thumbnail offset is stored as a long, but its data is a binary + // blob (not a slice of longs). + ThumbnailOffsetTagId: struct{}{}, + } - // tagsWithoutAlignment is a tag-lookup for tags whose value size won't - // necessarily be a multiple of its tag-type. - tagsWithoutAlignment = map[uint16]struct{} { - // The thumbnail offset is stored as a long, but its data is a binary - // blob (not a slice of longs). - ThumbnailOffsetTagId: struct{}{}, - } - - tagIndex *TagIndex + tagIndex *TagIndex ) var ( - tagsLogger = log.NewLogger("exif.tags") + tagsLogger = log.NewLogger("exif.tags") ) - // File structures. type encodedTag struct { - // id is signed, here, because YAML doesn't have enough information to - // support unsigned. - Id int `yaml:"id"` - Name string `yaml:"name"` - TypeName string `yaml:"type_name"` + // id is signed, here, because YAML doesn't have enough information to + // support unsigned. + Id int `yaml:"id"` + Name string `yaml:"name"` + TypeName string `yaml:"type_name"` } - // Indexing structures. type IndexedTag struct { - Id uint16 - Name string - Ifd string - Type uint16 + Id uint16 + Name string + Ifd string + Type uint16 } func (it IndexedTag) String() string { - return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.Ifd) + return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.Ifd) } func (it IndexedTag) IsName(ifd, name string) bool { - return it.Name == name && it.Ifd == ifd + return it.Name == name && it.Ifd == ifd } func (it IndexedTag) Is(ifd string, id uint16) bool { - return it.Id == id && it.Ifd == ifd + return it.Id == id && it.Ifd == ifd } type TagIndex struct { - tagsByIfd map[string]map[uint16]*IndexedTag - tagsByIfdR map[string]map[string]*IndexedTag + tagsByIfd map[string]map[uint16]*IndexedTag + tagsByIfdR map[string]map[string]*IndexedTag } func NewTagIndex() *TagIndex { - ti := new(TagIndex) - return ti + ti := new(TagIndex) + return ti } func (ti *TagIndex) load() (err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - // Already loaded. - if ti.tagsByIfd != nil { - return nil - } + // Already loaded. + if ti.tagsByIfd != nil { + return nil + } + // Read static data. - // Read static data. + encodedIfds := make(map[string][]encodedTag) - encodedIfds := make(map[string][]encodedTag) + err = yaml.Unmarshal([]byte(tagsYaml), encodedIfds) + log.PanicIf(err) - err = yaml.Unmarshal([]byte(tagsYaml), encodedIfds) - log.PanicIf(err) + // Load structure. + tagsByIfd := make(map[string]map[uint16]*IndexedTag) + tagsByIfdR := make(map[string]map[string]*IndexedTag) - // Load structure. + count := 0 + for ifdName, tags := range encodedIfds { + for _, tagInfo := range tags { + tagId := uint16(tagInfo.Id) + tagName := tagInfo.Name + tagTypeName := tagInfo.TypeName - tagsByIfd := make(map[string]map[uint16]*IndexedTag) - tagsByIfdR := make(map[string]map[string]*IndexedTag) + // TODO(dustin): !! Non-standard types, but found in real data. Ignore for right now. + if tagTypeName == "SSHORT" || tagTypeName == "FLOAT" || tagTypeName == "DOUBLE" { + continue + } - count := 0 - for ifdName, tags := range encodedIfds { - for _, tagInfo := range tags { - tagId := uint16(tagInfo.Id) - tagName := tagInfo.Name - tagTypeName := tagInfo.TypeName + tagTypeId, found := TypeNamesR[tagTypeName] + if found == false { + log.Panicf("type [%s] for [%s] not valid", tagTypeName, tagName) + continue + } -// TODO(dustin): !! Non-standard types, but found in real data. Ignore for right now. -if tagTypeName == "SSHORT" || tagTypeName == "FLOAT" || tagTypeName == "DOUBLE" { - continue -} + tag := &IndexedTag{ + Ifd: ifdName, + Id: tagId, + Name: tagName, + Type: tagTypeId, + } - tagTypeId, found := TypeNamesR[tagTypeName] - if found == false { - log.Panicf("type [%s] for [%s] not valid", tagTypeName, tagName) - continue - } + // Store by ID. - tag := &IndexedTag{ - Ifd: ifdName, - Id: tagId, - Name: tagName, - Type: tagTypeId, - } + family, found := tagsByIfd[ifdName] + if found == false { + family = make(map[uint16]*IndexedTag) + tagsByIfd[ifdName] = family + } + if _, found := family[tagId]; found == true { + log.Panicf("tag-ID defined more than once for IFD [%s]: (%02x)", ifdName, tagId) + } - // Store by ID. + family[tagId] = tag - family, found := tagsByIfd[ifdName] - if found == false { - family = make(map[uint16]*IndexedTag) - tagsByIfd[ifdName] = family - } + // Store by name. - if _, found := family[tagId]; found == true { - log.Panicf("tag-ID defined more than once for IFD [%s]: (%02x)", ifdName, tagId) - } + familyR, found := tagsByIfdR[ifdName] + if found == false { + familyR = make(map[string]*IndexedTag) + tagsByIfdR[ifdName] = familyR + } - family[tagId] = tag + if _, found := familyR[tagName]; found == true { + log.Panicf("tag-name defined more than once for IFD [%s]: (%s)", ifdName, tagName) + } + familyR[tagName] = tag - // Store by name. + count++ + } + } - familyR, found := tagsByIfdR[ifdName] - if found == false { - familyR = make(map[string]*IndexedTag) - tagsByIfdR[ifdName] = familyR - } + ti.tagsByIfd = tagsByIfd + ti.tagsByIfdR = tagsByIfdR - if _, found := familyR[tagName]; found == true { - log.Panicf("tag-name defined more than once for IFD [%s]: (%s)", ifdName, tagName) - } + tagsLogger.Debugf(nil, "(%d) tags loaded.", count) - familyR[tagName] = tag - - count++ - } - } - - ti.tagsByIfd = tagsByIfd - ti.tagsByIfdR = tagsByIfdR - - tagsLogger.Debugf(nil, "(%d) tags loaded.", count) - - return nil + return nil } // Get returns information about the non-IFD tag. func (ti *TagIndex) Get(ii IfdIdentity, id uint16) (it *IndexedTag, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - err = ti.load() - log.PanicIf(err) + err = ti.load() + log.PanicIf(err) - family, found := ti.tagsByIfd[ii.IfdName] - if found == false { - log.Panic(ErrTagNotFound) - } + family, found := ti.tagsByIfd[ii.IfdName] + if found == false { + log.Panic(ErrTagNotFound) + } - it, found = family[id] - if found == false { - log.Panic(ErrTagNotFound) - } + it, found = family[id] + if found == false { + log.Panic(ErrTagNotFound) + } - return it, nil + return it, nil } // Get returns information about the non-IFD tag. func (ti *TagIndex) GetWithName(ii IfdIdentity, name string) (it *IndexedTag, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - err = ti.load() - log.PanicIf(err) + err = ti.load() + log.PanicIf(err) - it, found := ti.tagsByIfdR[ii.IfdName][name] - if found != true { - log.Panic(ErrTagNotFound) - } + it, found := ti.tagsByIfdR[ii.IfdName][name] + if found != true { + log.Panic(ErrTagNotFound) + } - return it, nil + return it, nil } - // IfdTagWithId returns true if the given tag points to a child IFD block. func IfdTagNameWithIdOrFail(parentIfdName string, tagId uint16) string { - name, found := IfdTagNameWithId(parentIfdName, tagId) - if found == false { - log.Panicf("tag-ID (0x%02x) under parent IFD [%s] not associated with a child IFD", tagId, parentIfdName) - } + name, found := IfdTagNameWithId(parentIfdName, tagId) + if found == false { + log.Panicf("tag-ID (0x%02x) under parent IFD [%s] not associated with a child IFD", tagId, parentIfdName) + } - return name + return name } // IfdTagWithId returns true if the given tag points to a child IFD block. @@ -244,88 +233,80 @@ func IfdTagNameWithIdOrFail(parentIfdName string, tagId uint16) string { // TODO(dustin): !! Rewrite to take an IfdIdentity, instead. We shouldn't expect that IFD names are globally unique. func IfdTagNameWithId(parentIfdName string, tagId uint16) (name string, found bool) { - if tags, found := IfdTagNames[parentIfdName]; found == true { - if name, found = tags[tagId]; found == true { - return name, true - } - } + if tags, found := IfdTagNames[parentIfdName]; found == true { + if name, found = tags[tagId]; found == true { + return name, true + } + } - return "", false + return "", false } // IfdTagIdWithIdentity returns true if the given tag points to a child IFD // block. func IfdTagIdWithIdentity(ii IfdIdentity) (tagId uint16, found bool) { - if tags, found := IfdTagIds[ii.ParentIfdName]; found == true { - if tagId, found = tags[ii.IfdName]; found == true { - return tagId, true - } - } + if tags, found := IfdTagIds[ii.ParentIfdName]; found == true { + if tagId, found = tags[ii.IfdName]; found == true { + return tagId, true + } + } - return 0, false + return 0, false } func IfdTagIdWithIdentityOrFail(ii IfdIdentity) (tagId uint16) { - if tags, found := IfdTagIds[ii.ParentIfdName]; found == true { - if tagId, found = tags[ii.IfdName]; found == true { - if tagId == 0 { - // This IFD is not the type that can be linked to from a tag. - log.Panicf("not a child IFD: [%s]", ii.IfdName) - } + if tags, found := IfdTagIds[ii.ParentIfdName]; found == true { + if tagId, found = tags[ii.IfdName]; found == true { + if tagId == 0 { + // This IFD is not the type that can be linked to from a tag. + log.Panicf("not a child IFD: [%s]", ii.IfdName) + } - return tagId - } - } + return tagId + } + } - log.Panicf("no tag for invalid IFD identity: %v", ii) - return 0 + log.Panicf("no tag for invalid IFD identity: %v", ii) + return 0 } func IfdIdWithIdentity(ii IfdIdentity) int { - id, _ := IfdIds[ii] - return id + id, _ := IfdIds[ii] + return id } func IfdIdWithIdentityOrFail(ii IfdIdentity) int { - id, _ := IfdIds[ii] - if id == 0 { - log.Panicf("IFD not valid: %v", ii) - } + id, _ := IfdIds[ii] + if id == 0 { + log.Panicf("IFD not valid: %v", ii) + } - return id + return id } func IfdIdOrFail(parentIfdName, ifdName string) (ii IfdIdentity, id int) { - ii, id = IfdId(parentIfdName, ifdName) - if id == 0 { - log.Panicf("IFD is not valid: [%s] [%s]", parentIfdName, ifdName) - } + ii, id = IfdId(parentIfdName, ifdName) + if id == 0 { + log.Panicf("IFD is not valid: [%s] [%s]", parentIfdName, ifdName) + } - return ii, id + return ii, id } func IfdId(parentIfdName, ifdName string) (ii IfdIdentity, id int) { - ii = IfdIdentity{ - ParentIfdName: parentIfdName, - IfdName: ifdName, - } + ii = IfdIdentity{ + ParentIfdName: parentIfdName, + IfdName: ifdName, + } - id, found := IfdIds[ii] - if found != true { - return IfdIdentity{}, 0 - } + id, found := IfdIds[ii] + if found != true { + return IfdIdentity{}, 0 + } - return ii, id + return ii, id } func init() { - goPath := os.Getenv("GOPATH") - if goPath == "" { - log.Panicf("GOPATH is empty") - } - - assetsPath := path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets") - tagDataFilepath = path.Join(assetsPath, "tags.yaml") - - tagIndex = NewTagIndex() + tagIndex = NewTagIndex() } diff --git a/test_common.go b/test_common.go deleted file mode 100644 index 4e6ba09..0000000 --- a/test_common.go +++ /dev/null @@ -1,36 +0,0 @@ -package exif - -import ( - "os" - "path" - - "encoding/binary" - "io/ioutil" - - "github.com/dsoprea/go-logging" -) - -var ( - TestDefaultByteOrder = binary.BigEndian - - assetsPath = "" - testExifData = make([]byte, 0) -) - -func init() { - goPath := os.Getenv("GOPATH") - if goPath == "" { - log.Panicf("GOPATH is empty") - } - - assetsPath = path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets") - - - // Load test EXIF data. - - filepath := path.Join(assetsPath, "NDM_8901.jpg.exif") - - var err error - testExifData, err = ioutil.ReadFile(filepath) - log.PanicIf(err) -}