1
0
mirror of https://github.com/dsoprea/go-exif.git synced 2025-04-27 21:22:59 +00:00

ifd: Formalized visitor implementation.

This commit is contained in:
Dustin Oprea 2018-04-14 20:32:02 -04:00
parent 05fe72f929
commit 315ca60f03
3 changed files with 44 additions and 65 deletions

21
exif.go

@ -1,7 +1,6 @@
package exif package exif
import ( import (
"fmt"
"errors" "errors"
"bytes" "bytes"
@ -31,7 +30,7 @@ func (e *Exif) IsExif(data []byte) (ok bool) {
return false return false
} }
func (e *Exif) Parse(data []byte) (err error) { func (e *Exif) Parse(data []byte, visitor TagVisitor) (err error) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err = log.Wrap(state.(error)) err = log.Wrap(state.(error))
@ -39,10 +38,6 @@ func (e *Exif) Parse(data []byte) (err error) {
}() }()
if e.IsExif(data) == false { if e.IsExif(data) == false {
// TODO(dustin): !! Debugging.
fmt.Printf("AppData doesn't look like EXIF. BYTES=(%d)\n", len(data))
return ErrNotExif return ErrNotExif
} }
@ -51,7 +46,6 @@ func (e *Exif) Parse(data []byte) (err error) {
// CIPA DC-008-2016; JEITA CP-3451D // CIPA DC-008-2016; JEITA CP-3451D
// -> http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf // -> http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
fmt.Printf("AppData DOES look like EXIF. BYTES=(%d)\n", len(data))
byteOrderSignature := data[6:8] byteOrderSignature := data[6:8]
byteOrder := IfdByteOrder(BigEndianByteOrder) byteOrder := IfdByteOrder(BigEndianByteOrder)
if string(byteOrderSignature) == "II" { if string(byteOrderSignature) == "II" {
@ -60,15 +54,9 @@ fmt.Printf("AppData DOES look like EXIF. BYTES=(%d)\n", len(data))
log.Panicf("byte-order not recognized: [%v]", byteOrderSignature) log.Panicf("byte-order not recognized: [%v]", byteOrderSignature)
} }
fmt.Printf("BYTE-ORDER: [%s]\n", byteOrderSignature)
fixedBytes := data[8:10] fixedBytes := data[8:10]
if fixedBytes[0] != 0x2a || fixedBytes[1] != 0x00 { if fixedBytes[0] != 0x2a || fixedBytes[1] != 0x00 {
exifLogger.Warningf(nil, "EXIF app-data header fixed-bytes should be 0x002a but are: [%v]", fixedBytes) exifLogger.Warningf(nil, "EXIF app-data header fixed-bytes should be 0x002a but are: [%v]", fixedBytes)
// TODO(dustin): Debugging.
fmt.Printf("EXIF app-data header fixed-bytes should be 0x002a but are: [%v]\n", fixedBytes)
return nil return nil
} }
@ -81,13 +69,6 @@ fmt.Printf("AppData DOES look like EXIF. BYTES=(%d)\n", len(data))
ifd := NewIfd(data, byteOrder) ifd := NewIfd(data, byteOrder)
visitor := func() error {
// TODO(dustin): !! Debugging.
fmt.Printf("IFD visitor.\n")
return nil
}
err = ifd.Scan(visitor, firstIfdOffset) err = ifd.Scan(visitor, firstIfdOffset)
log.PanicIf(err) log.PanicIf(err)

@ -4,6 +4,7 @@ import (
"testing" "testing"
"os" "os"
"path" "path"
"fmt"
"io/ioutil" "io/ioutil"
@ -62,7 +63,32 @@ func TestParse(t *testing.T) {
// Run the parse. // Run the parse.
err = e.Parse(data[foundAt:]) ti := NewTagIndex()
visitor := func(tagId, tagType uint16, tagCount, valueOffset uint32) (err error) {
it, err := ti.GetWithTagId(tagId)
if err != nil {
if err == ErrTagNotFound {
return nil
} else {
log.Panic(err)
}
}
fmt.Printf("Tag: ID=(0x%04x) NAME=[%s] IFD=[%s] TYPE=(%d) COUNT=(%d) VALUE-OFFSET=(%d)\n", tagId, it.Name, it.Ifd, tagType, tagCount, valueOffset)
// Notes on the tag-value's value (we'll have to use this as a pointer if the type potentially requires more than four bytes):
//
// This tag records the offset from the start of the TIFF header to the position where the value itself is
// recorded. In cases where the value fits in 4 Bytes, the value itself is recorded. If the value is smaller
// than 4 Bytes, the value is stored in the 4-Byte area starting from the left, i.e., from the lower end of
// the byte offset area. For example, in big endian format, if the type is SHORT and the value is 1, it is
// recorded as 00010000.H
return nil
}
err = e.Parse(data[foundAt:], visitor)
log.PanicIf(err) log.PanicIf(err)
} }

60
ifd.go

@ -1,7 +1,6 @@
package exif package exif
import ( import (
"fmt"
"bytes" "bytes"
"io" "io"
@ -102,9 +101,12 @@ func (ifd *Ifd) getUint32() (value uint32, err error) {
return value, nil return value, nil
} }
type TagVisitor func(tagId, tagType uint16, tagCount, valueOffset uint32) (err error)
// parseCurrentIfd decodes the IFD block that we're currently sitting on the // parseCurrentIfd decodes the IFD block that we're currently sitting on the
// first byte of. // first byte of.
func (ifd *Ifd) parseCurrentIfd() (nextIfdOffset uint32, err error) { func (ifd *Ifd) parseCurrentIfd(visitor TagVisitor) (nextIfdOffset uint32, err error) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err = log.Wrap(state.(error)) err = log.Wrap(state.(error))
@ -115,62 +117,34 @@ func (ifd *Ifd) parseCurrentIfd() (nextIfdOffset uint32, err error) {
tagCount, err := ifd.getUint16() tagCount, err := ifd.getUint16()
log.PanicIf(err) log.PanicIf(err)
fmt.Printf("IFD: TOTAL TAG COUNT=(%02x)\n", tagCount) ifdLogger.Debugf(nil, "Current IFD tag-count: (%d)", tagCount)
t := NewTagIndex()
for i := uint16(0); i < tagCount; i++ { for i := uint16(0); i < tagCount; i++ {
// TODO(dustin): !! 0x8769 tag-IDs are child IFDs.
// TODO(dustin): !! 0x8769 tag-IDs are child IFDs. We need to be able to recurse.
tagId, err := ifd.getUint16() tagId, err := ifd.getUint16()
log.PanicIf(err) log.PanicIf(err)
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() tagType, err := ifd.getUint16()
log.PanicIf(err) log.PanicIf(err)
fmt.Printf("IFD: Tag (%d) TYPE=(%d)\n", i, tagType)
tagCount, err := ifd.getUint32() tagCount, err := ifd.getUint32()
log.PanicIf(err) log.PanicIf(err)
fmt.Printf("IFD: Tag (%d) COUNT=(%02x)\n", i, tagCount)
valueOffset, err := ifd.getUint32() valueOffset, err := ifd.getUint32()
log.PanicIf(err) log.PanicIf(err)
fmt.Printf("IFD: Tag (%d) VALUE-OFFSET=(%x)\n", i, valueOffset) if visitor != nil {
err := visitor(tagId, tagType, tagCount, valueOffset)
// Notes on the tag-value's value (we'll have to use this as a pointer if the type potentially requires more than four bytes): log.PanicIf(err)
// }
// This tag records the offset from the start of the TIFF header to the position where the value itself is
// recorded. In cases where the value fits in 4 Bytes, the value itself is recorded. If the value is smaller
// than 4 Bytes, the value is stored in the 4-Byte area starting from the left, i.e., from the lower end of
// the byte offset area. For example, in big endian format, if the type is SHORT and the value is 1, it is
// recorded as 00010000.H
} }
fmt.Printf("\n")
nextIfdOffset, err = ifd.getUint32() nextIfdOffset, err = ifd.getUint32()
log.PanicIf(err) log.PanicIf(err)
fmt.Printf("IFD: NEXT-IFD-OFFSET=(%x)\n", nextIfdOffset) ifdLogger.Debugf(nil, "Next IFD at offset: (%08x)", nextIfdOffset)
fmt.Printf("\n")
return nextIfdOffset, nil return nextIfdOffset, nil
} }
@ -184,7 +158,7 @@ func (ifd *Ifd) forwardToIfd(ifdOffset uint32) (err error) {
} }
}() }()
fmt.Printf("IFD: Forwarding to IFD. TOP-OFFSET=(%d) IFD-OFFSET=(%d)\n", ifd.ifdTopOffset, ifdOffset) ifdLogger.Debugf(nil, "Forwarding to IFD. TOP-OFFSET=(%d) IFD-OFFSET=(%d)", ifd.ifdTopOffset, ifdOffset)
nextOffset := ifd.ifdTopOffset + ifdOffset nextOffset := ifd.ifdTopOffset + ifdOffset
@ -198,10 +172,8 @@ func (ifd *Ifd) forwardToIfd(ifdOffset uint32) (err error) {
return nil return nil
} }
type IfdVisitor func() error
// Scan enumerates the different EXIF blocks (called IFDs). // Scan enumerates the different EXIF blocks (called IFDs).
func (ifd *Ifd) Scan(v IfdVisitor, firstIfdOffset uint32) (err error) { func (ifd *Ifd) Scan(visitor TagVisitor, firstIfdOffset uint32) (err error) {
defer func() { defer func() {
if state := recover(); state != nil { if state := recover(); state != nil {
err = log.Wrap(state.(error)) err = log.Wrap(state.(error))
@ -212,7 +184,7 @@ func (ifd *Ifd) Scan(v IfdVisitor, firstIfdOffset uint32) (err error) {
log.PanicIf(err) log.PanicIf(err)
for { for {
nextIfdOffset, err := ifd.parseCurrentIfd() nextIfdOffset, err := ifd.parseCurrentIfd(visitor)
log.PanicIf(err) log.PanicIf(err)
if nextIfdOffset == 0 { if nextIfdOffset == 0 {