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:
parent
05fe72f929
commit
315ca60f03
21
exif.go
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)
|
||||||
|
|
||||||
|
28
exif_test.go
28
exif_test.go
@ -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
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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user