mirror of https://github.com/dsoprea/go-exif.git
ifd_enumerate: Now parse and expose thumbnail.
parent
d7ff9ccbbe
commit
c93f37a85d
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
102
ifd_enumerate.go
102
ifd_enumerate.go
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"errors"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
|
@ -12,6 +13,8 @@ import (
|
|||
|
||||
var (
|
||||
ifdEnumerateLogger = log.NewLogger("exifjpeg.ifd")
|
||||
|
||||
ErrNoThumbnail = errors.New("no thumbnail")
|
||||
)
|
||||
|
||||
|
||||
|
@ -293,6 +296,8 @@ type Ifd struct {
|
|||
Children []*Ifd
|
||||
NextIfdOffset uint32
|
||||
NextIfd *Ifd
|
||||
|
||||
thumbnailData []byte
|
||||
}
|
||||
|
||||
func (ifd *Ifd) TagValue(ite *IfdTagEntry) (value interface{}, err error) {
|
||||
|
@ -323,7 +328,7 @@ func (ifd *Ifd) TagValueBytes(ite *IfdTagEntry) (value []byte, err error) {
|
|||
|
||||
// FindTagWithId returns a list of tags (usually just zero or one) that match
|
||||
// the given tag ID. This is efficient.
|
||||
func (ifd Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
|
||||
func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
|
@ -340,7 +345,7 @@ func (ifd Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
|
|||
|
||||
// FindTagWithName returns a list of tags (usually just zero or one) that match
|
||||
// the given tag name. This is not efficient (though the labor is trivial).
|
||||
func (ifd Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error) {
|
||||
func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
|
@ -380,11 +385,83 @@ func (ifd Ifd) String() string {
|
|||
return fmt.Sprintf("Ifd<ID=(%d) PARENT-IFD=[%s] IFD=[%s] IDX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)", ifd.Id, ifd.Ii.ParentIfdName, ifd.Ii.IfdName, ifd.Index, len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset)
|
||||
}
|
||||
|
||||
func (ifd Ifd) Identity() IfdIdentity {
|
||||
func (ifd *Ifd) parseThumbnail() (err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
results, err := ifd.FindTagWithId(ThumbnailOffsetTagId)
|
||||
if err != nil {
|
||||
if log.Is(err, ErrTagNotFound) == true {
|
||||
return nil
|
||||
} else {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
offsetIte := results[0]
|
||||
|
||||
vRaw, err := ifd.TagValue(offsetIte)
|
||||
log.PanicIf(err)
|
||||
|
||||
vList := vRaw.([]uint32)
|
||||
if len(vList) != 1 {
|
||||
log.Panicf("not exactly one long: (%d)", len(vList))
|
||||
}
|
||||
|
||||
offset := vList[0]
|
||||
|
||||
results, err = ifd.FindTagWithId(ThumbnailSizeTagId)
|
||||
if err != nil {
|
||||
if log.Is(err, ErrTagNotFound) == true {
|
||||
log.Panic(ErrNoThumbnail)
|
||||
} else {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
lengthIte := results[0]
|
||||
|
||||
vRaw, err = ifd.TagValue(lengthIte)
|
||||
log.PanicIf(err)
|
||||
|
||||
vList = vRaw.([]uint32)
|
||||
if len(vList) != 1 {
|
||||
log.Panicf("not exactly one long: (%d)", len(vList))
|
||||
}
|
||||
|
||||
length := vList[0]
|
||||
|
||||
if len(ifd.addressableData) < int(offset) + int(length) {
|
||||
log.Panicf("thumbnail size not valid: (%d) > (%d)", length, len(ifd.addressableData) - int(offset))
|
||||
}
|
||||
|
||||
ifd.thumbnailData = ifd.addressableData[offset:offset + length]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ifd *Ifd) Thumbnail() (data []byte, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
if ifd.thumbnailData == nil {
|
||||
log.Panic(ErrNoThumbnail)
|
||||
}
|
||||
|
||||
return ifd.thumbnailData, nil
|
||||
}
|
||||
|
||||
func (ifd *Ifd) Identity() IfdIdentity {
|
||||
return ifd.Ii
|
||||
}
|
||||
|
||||
func (ifd Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
|
||||
func (ifd *Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
|
||||
if tags == nil {
|
||||
tags = make([]*IfdTagEntry, 0)
|
||||
}
|
||||
|
@ -427,11 +504,11 @@ func (ifd Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
|
|||
}
|
||||
|
||||
// DumpTags prints the IFD hierarchy.
|
||||
func (ifd Ifd) DumpTags() []*IfdTagEntry {
|
||||
func (ifd *Ifd) DumpTags() []*IfdTagEntry {
|
||||
return ifd.dumpTags(nil)
|
||||
}
|
||||
|
||||
func (ifd Ifd) printTagTree(index, level int, nextLink bool) {
|
||||
func (ifd *Ifd) printTagTree(index, level int, nextLink bool) {
|
||||
indent := strings.Repeat(" ", level * 2)
|
||||
|
||||
prefix := " "
|
||||
|
@ -489,11 +566,11 @@ func (ifd Ifd) printTagTree(index, level int, nextLink bool) {
|
|||
}
|
||||
|
||||
// PrintTagTree prints the IFD hierarchy.
|
||||
func (ifd Ifd) PrintTagTree() {
|
||||
func (ifd *Ifd) PrintTagTree() {
|
||||
ifd.printTagTree(0, 0, false)
|
||||
}
|
||||
|
||||
func (ifd Ifd) printIfdTree(level int, nextLink bool) {
|
||||
func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
|
||||
indent := strings.Repeat(" ", level * 2)
|
||||
|
||||
prefix := " "
|
||||
|
@ -537,11 +614,11 @@ func (ifd Ifd) printIfdTree(level int, nextLink bool) {
|
|||
}
|
||||
|
||||
// PrintIfdTree prints the IFD hierarchy.
|
||||
func (ifd Ifd) PrintIfdTree() {
|
||||
func (ifd *Ifd) PrintIfdTree() {
|
||||
ifd.printIfdTree(0, false)
|
||||
}
|
||||
|
||||
func (ifd Ifd) dumpTree(tagsDump []string, level int) []string {
|
||||
func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string {
|
||||
if tagsDump == nil {
|
||||
tagsDump = make([]string, 0)
|
||||
}
|
||||
|
@ -599,7 +676,7 @@ func (ifd Ifd) dumpTree(tagsDump []string, level int) []string {
|
|||
}
|
||||
|
||||
// DumpTree returns a list of strings describing the IFD hierarchy.
|
||||
func (ifd Ifd) DumpTree() []string {
|
||||
func (ifd *Ifd) DumpTree() []string {
|
||||
return ifd.dumpTree(nil, 0)
|
||||
}
|
||||
|
||||
|
@ -691,6 +768,9 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
|
|||
NextIfdOffset: nextIfdOffset,
|
||||
}
|
||||
|
||||
err = ifd.parseThumbnail()
|
||||
log.PanicIf(err)
|
||||
|
||||
// Add ourselves to a big list of IFDs.
|
||||
ifds = append(ifds, &ifd)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"bytes"
|
||||
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
@ -277,3 +278,34 @@ func Test_Ifd_FindTagWithName_NonStandard(t *testing.T) {
|
|||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Ifd_Thumbnail(t *testing.T) {
|
||||
filepath := path.Join(assetsPath, "NDM_8901.jpg")
|
||||
|
||||
e := NewExif()
|
||||
|
||||
rawExif, err := e.SearchAndExtractExif(filepath)
|
||||
log.PanicIf(err)
|
||||
|
||||
_, index, err := e.Collect(rawExif)
|
||||
log.PanicIf(err)
|
||||
|
||||
ifd := index.RootIfd
|
||||
|
||||
if ifd.NextIfd == nil {
|
||||
t.Fatalf("There is no IFD1.")
|
||||
}
|
||||
|
||||
// The thumbnail is in IFD1 (The second root IFD).
|
||||
actual, err := ifd.NextIfd.Thumbnail()
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedFilepath := path.Join(assetsPath, "NDM_8901.jpg.thumbnail")
|
||||
|
||||
expected, err := ioutil.ReadFile(expectedFilepath)
|
||||
log.PanicIf(err)
|
||||
|
||||
if bytes.Compare(actual, expected) != 0 {
|
||||
t.Fatalf("thumbnail not correct")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue