diff --git a/assets/tags.yaml b/assets/tags.yaml new file mode 100644 index 0000000..f3992d2 --- /dev/null +++ b/assets/tags.yaml @@ -0,0 +1,31 @@ +Image: + - id: 0x103 + name: Compression + - id: 0x10f + name: Make + - id: 0x110 + name: Model + - id: 0x112 + name: Orientation + - id: 0x11a + name: XResolution + - id: 0x11b + name: YResolution + - id: 0x128 + name: ResolutionUnit + - id: 0x132 + name: DateTime + - id: 0x13b + name: Artist + - id: 0x201 + name: JPEGInterchangeFormat + - id: 0x202 + name: JPEGInterchangeFormatLength + - id: 0x213 + name: YCbCrPositioning + - id: 0x8298 + name: Copyright + - id: 0x8769 + name: ExifTag + - id: 0x8825 + name: GPSTag diff --git a/exif_test.go b/exif_test.go index 2ed915b..bdaafdc 100644 --- a/exif_test.go +++ b/exif_test.go @@ -74,4 +74,3 @@ func init() { assetsPath = path.Join(goPath, "src", "github.com", "dsoprea", "go-exif", "assets") } - diff --git a/ifd.go b/ifd.go index 272c037..fe2a3cb 100644 --- a/ifd.go +++ b/ifd.go @@ -117,12 +117,23 @@ func (ifd *Ifd) parseCurrentIfd() (nextIfdOffset uint32, err error) { fmt.Printf("IFD: TOTAL TAG COUNT=(%02x)\n", tagCount) + t := NewTagIndex() + for i := uint16(0); i < tagCount; i++ { // TODO(dustin): !! 0x8769 tag-IDs are child IFDs. tagId, err := ifd.getUint16() log.PanicIf(err) - fmt.Printf("IFD: Tag (%d) ID=(%02x)\n", i, tagId) + it, err := t.GetWithTagId(tagId) + if err != nil { + if err == ErrTagNotFound { + log.Panicf("tag (%04x) not known") + } else { + log.Panic(err) + } + } + + fmt.Printf("IFD: Tag (%d) ID=(%02x) NAME=[%s] IFD=[%s]\n", i, tagId, it.Name, it.Ifd) tagType, err := ifd.getUint16() diff --git a/tags.go b/tags.go new file mode 100644 index 0000000..ef5ed09 --- /dev/null +++ b/tags.go @@ -0,0 +1,157 @@ +package exif + +import ( + "os" + "path" + "fmt" + "errors" + + "io/ioutil" + + "gopkg.in/yaml.v2" + "github.com/dsoprea/go-logging" +) + +var ( + tagDataFilepath = "" +) + +var ( + tagsLogger = log.NewLogger("exif.tags") + ErrTagNotFound = errors.New("tag not found") +) + + +// 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"` +} + + +// Indexing structures. + +type IndexedTag struct { + Id uint16 + Name string + Ifd string +} + +func (it IndexedTag) String() string { + return fmt.Sprintf("TAG", it.Id, it.Name, it.Ifd) +} + +func (it IndexedTag) IsName(ifd, name string) bool { + return it.Name == name && it.Ifd == ifd +} + +func (it IndexedTag) Is(id uint16) bool { + return it.Id == id +} + +type TagIndex struct { + tagsByIfd map[string]map[uint16]*IndexedTag + tagsById map[uint16]*IndexedTag +} + +func NewTagIndex() *TagIndex { + ti := new(TagIndex) + + err := ti.load() + log.PanicIf(err) + + return ti +} + +func (ti *TagIndex) load() (err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + + // Read static data. + + f, err := os.Open(tagDataFilepath) + log.PanicIf(err) + + data, err := ioutil.ReadAll(f) + log.PanicIf(err) + + encodedIfds := make(map[string][]encodedTag) + + err = yaml.Unmarshal(data, &encodedIfds) + log.PanicIf(err) + + + // Load structure. + + tagsById := make(map[uint16]*IndexedTag) + tagsByIfd := make(map[string]map[uint16]*IndexedTag) + + count := 0 + for ifdName, tags := range encodedIfds { + for _, tagInfo := range tags { + tagId := uint16(tagInfo.Id) + tagName := tagInfo.Name + + tag := &IndexedTag{ + Ifd: ifdName, + Id: tagId, + Name: tagName, + } + + if _, found := tagsById[tagId]; found == true { + log.Panicf("tag-ID defined more than once: (%02x)", tagId) + } + + tagsById[tagId] = tag + + family, found := tagsByIfd[ifdName] + if found == false { + family = make(map[uint16]*IndexedTag) + tagsByIfd[ifdName] = family + } + + family[tagId] = tag + + count++ + } + } + + ti.tagsById = tagsById + ti.tagsByIfd = tagsByIfd + + tagsLogger.Debugf(nil, "(%d) tags loaded.", count) + + return nil +} + +func (ti *TagIndex) GetWithTagId(id uint16) (it *IndexedTag, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + it, found := ti.tagsById[id] + if found == false { + return nil, ErrTagNotFound + } + + return it, nil +} + +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") +} diff --git a/tags_test.go b/tags_test.go new file mode 100644 index 0000000..340fc5d --- /dev/null +++ b/tags_test.go @@ -0,0 +1,18 @@ +package exif + +import ( + "testing" + + "github.com/dsoprea/go-logging" +) + +func TestGetWithTagId(t *testing.T) { + ti := NewTagIndex() + + it, err := ti.GetWithTagId(0x10f) + log.PanicIf(err) + + if it.Is(0x10f) == false || it.IsName("Image", "Make") == false { + t.Fatalf("tag info not correct") + } +}