diff --git a/ifd_builder.go b/ifd_builder.go index 54ce9eb..8ecfd04 100644 --- a/ifd_builder.go +++ b/ifd_builder.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "bytes" + "strings" "encoding/binary" @@ -20,20 +21,37 @@ var ( // TODO(dustin): !! Make sure we either replace existing IFDs or validate that the IFD doesn't already exist. +// TODO(dustin): !! Add test for NewIfdBuilderWithExistingIfd. type builderTag struct { + // ifdName is non-empty if represents a child-IFD. ifdName string + tagId uint16 // value is either a value that can be encoded, an IfdBuilder instance (for // child IFDs), or an IfdTagEntry instance representing an existing, // previously-stored tag. - valueBytes interface{} + value interface{} } func (bt builderTag) String() string { - return fmt.Sprintf("BuilderTag", bt.tagId, bt.ifdName, bt.valueBytes) + valuePhrase := "" + switch bt.value.(type) { + case []byte: + valueBytes := bt.value.([]byte) + + if len(valueBytes) <= 8 { + valuePhrase = fmt.Sprintf("%v", valueBytes) + } else { + valuePhrase = fmt.Sprintf("%v...", valueBytes[:8]) + } + default: + valuePhrase = fmt.Sprintf("%v", bt.value) + } + + return fmt.Sprintf("BuilderTag", bt.tagId, bt.ifdName, valuePhrase) } @@ -57,21 +75,12 @@ type IfdBuilder struct { } func NewIfdBuilder(ifdName string, byteOrder binary.ByteOrder) (ib *IfdBuilder) { - found := false - for _, validName := range validIfds { - if validName == ifdName { - found = true - break - } - } - - if found == false { - log.Panicf("ifd not found: [%s]", ifdName) - } - ib = &IfdBuilder{ ifdName: ifdName, + + // ifdName is empty unless it's a child-IFD. ifdTagId: IfdTagIds[ifdName], + byteOrder: byteOrder, tags: make([]builderTag, 0), } @@ -79,16 +88,65 @@ func NewIfdBuilder(ifdName string, byteOrder binary.ByteOrder) (ib *IfdBuilder) return ib } -func NewIfdBuilderWithExistingIfd(ifd *Ifd, byteOrder binary.ByteOrder) (ib *IfdBuilder) { +// NewIfdBuilderWithExistingIfd creates a new IB using the same header type +// information as the given IFD. +func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) { + ifdTagId, found := IfdTagIds[ifd.Name] + if found == false { + log.Panicf("tag-ID for IFD not found: [%s]", ifd.Name) + } + ib = &IfdBuilder{ ifdName: ifd.Name, - byteOrder: byteOrder, + ifdTagId: ifdTagId, + byteOrder: ifd.ByteOrder, existingOffset: ifd.Offset, } return ib } +// NewIfdBuilderFromExistingChain creates a chain of IB instances from an +// IFD chain generated from real data. +func NewIfdBuilderFromExistingChain(rootIfd *Ifd, exifData []byte) (rootIb *IfdBuilder) { + itevr := NewIfdTagEntryValueResolver(exifData, rootIfd.ByteOrder) + +// TODO(dustin): !! When we actually write the code to flatten the IB to bytes, make sure to skip the tags that have a nil value (which will happen when we add-from-exsting without a resolver instance). + + var newIb *IfdBuilder + for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd { + lastIb := newIb + + ifdName := thisExistingIfd.Name + if ifdName == "" { + ifdName = IfdStandard + } + + newIb = NewIfdBuilder(ifdName, binary.BigEndian) + if lastIb != nil { + lastIb.SetNextIfd(newIb) + } + + if rootIb == nil { + rootIb = newIb + } + + err := newIb.AddTagsFromExisting(thisExistingIfd, itevr, nil, nil) + log.PanicIf(err) + + // Any child IFDs will still not be copied. Do that now. + + for _, childIfd := range thisExistingIfd.Children { + childIb := NewIfdBuilderFromExistingChain(childIfd, exifData) + + err = newIb.AddChildIb(childIb) + log.PanicIf(err) + } + } + + return rootIb +} + func (ib *IfdBuilder) String() string { nextIfdPhrase := "" if ib.nextIfd != nil { @@ -114,6 +172,90 @@ func (ioi *ifdOffsetIterator) Offset() uint32 { return ioi.offset } +func (ib *IfdBuilder) Tags() (tags []builderTag) { + return ib.tags +} + +func (ib *IfdBuilder) dump(levels int) { + indent := strings.Repeat(" ", levels * 4) + + if levels == 0 { + fmt.Printf("%sIFD: %s\n", indent, ib) + } else { + fmt.Printf("%sChild IFD: %s\n", indent, ib) + } + + ti := NewTagIndex() + + if len(ib.tags) > 0 { + fmt.Printf("\n") + + for i, tag := range ib.tags { + _, isChildIb := IfdTagNames[tag.tagId] + + tagName := "" + + // If a normal tag (not a child IFD) get the name. + if isChildIb == true { + tagName = "" + } else { + it, err := ti.Get(tag.ifdName, tag.tagId) + if log.Is(err, ErrTagNotFound) == true { + tagName = "" + } else if err != nil { + log.Panic(err) + } else { + tagName = it.Name + } + } + + fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag) + + if isChildIb == true { + fmt.Printf("\n") + + childIb := tag.value.(*IfdBuilder) + childIb.dump(levels + 1) + } + } + + fmt.Printf("\n") + } +} + +func (ib *IfdBuilder) Dump() { + ib.dump(0) +} + +func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, lines []string) (linesOutput []string) { + if lines == nil { + linesOutput = make([]string, 0) + } else { + linesOutput = lines + } + + for i, tag := range thisIb.tags { + line := fmt.Sprintf(" IFD-TAG-ID=(0x%02x) CHILD-IFD=[%s] INDEX=(%d) TAG=[0x%02x]", prefix, thisIb.ifdName, thisIb.ifdTagId, tag.ifdName, i, tag.tagId) + linesOutput = append(linesOutput, line) + + if tag.ifdName != "" { + childPrefix := "" + if prefix == "" { + childPrefix = fmt.Sprintf("%s", thisIb.ifdName) + } else { + childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.ifdName) + } + + linesOutput = thisIb.dumpToStrings(tag.value.(*IfdBuilder), childPrefix, linesOutput) + } + } + + return linesOutput +} + +func (ib *IfdBuilder) DumpToStrings() (lines []string) { + return ib.dumpToStrings(ib, "", lines) +} // calculateRawTableSize returns the number of bytes required just to store the // basic IFD header and tags. This needs to be called before we can even write @@ -262,24 +404,6 @@ func (ib *IfdBuilder) BuildExif() (new []byte, err error) { return b.Bytes(), nil } -func (ib *IfdBuilder) Tags() (tags []builderTag) { - return ib.tags -} - -func (ib *IfdBuilder) Dump() { - fmt.Printf("IFD: %s\n", ib) - - if len(ib.tags) > 0 { - fmt.Printf("\n") - - for i, tag := range ib.tags { - fmt.Printf(" (%d): %s\n", i, tag) - } - - fmt.Printf("\n") - } -} - func (ib *IfdBuilder) SetNextIfd(nextIfd *IfdBuilder) (err error) { defer func() { if state := recover(); state != nil { @@ -437,43 +561,45 @@ func (ib *IfdBuilder) Add(bt builderTag) (err error) { } }() + if bt.value != nil { + switch bt.value.(type) { + case []byte: + default: + log.Panicf("tag value must be a byte-slice or a child IFD-builder: %v", bt) + } + } + ib.tags = append(ib.tags, bt) return nil } -func (ib *IfdBuilder) AddChildIfd(childIfd *IfdBuilder) (err error) { +func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) } }() -// TODO(dustin): !! We might not want to take an actual IfdBuilder instance, as -// these are mutable in nature (unless we definitely want to -// allow them to tbe chnaged right up until they're actually -// written). We might be better with a final, immutable tag -// container insted. - - if childIfd.ifdTagId == 0 { - log.Panicf("IFD [%s] can not be used as a child IFD (not associated with a tag-ID)") - } else if childIfd.byteOrder != ib.byteOrder { - log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIfd.byteOrder, ib.byteOrder) + if childIb.ifdTagId == 0 { + log.Panicf("IFD can not be used as a child IFD (not associated with a tag-ID): %v", childIb) + } else if childIb.byteOrder != ib.byteOrder { + log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIb.byteOrder, ib.byteOrder) } bt := builderTag{ - ifdName: childIfd.ifdName, - tagId: childIfd.ifdTagId, - valueBytes: childIfd, + ifdName: childIb.ifdName, + tagId: childIb.ifdTagId, + value: childIb, } - ib.Add(bt) + ib.tags = append(ib.tags, bt) return nil } // AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this // builder. It excludes child IFDs. This must be added explicitly via -// `AddChildIfd()`. +// `AddChildIb()`. func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResolver, includeTagIds []uint16, excludeTagIds []uint16) (err error) { defer func() { if state := recover(); state != nil { @@ -542,8 +668,15 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResol if itevr != nil { var err error - bt.valueBytes, err = itevr.ValueBytes(&ite) - log.PanicIf(err) + bt.value, err = itevr.ValueBytes(&ite) + if err != nil { + if log.Is(err, ErrUnhandledUnknownTypedTag) == true { + ifdBuilderLogger.Warningf(nil, "Unknown-type tag can't be parsed so it can't be copied to the new IFD.") + continue + } + + log.Panic(err) + } } err := ib.Add(bt) diff --git a/ifd_builder_test.go b/ifd_builder_test.go index 0ddeb0e..cf89800 100644 --- a/ifd_builder_test.go +++ b/ifd_builder_test.go @@ -3,6 +3,8 @@ package exif import ( "testing" "reflect" + "bytes" + "path" "encoding/binary" @@ -18,33 +20,37 @@ func TestAdd(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) - originalShorts := []uint16 { 0x111, 0x222, 0x333 } + originalBytes := []byte { 0x11, 0x22, 0x33 } bt = builderTag{ tagId: 0x44, - valueBytes: originalShorts, + value: originalBytes, } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) if ib.ifdName != IfdStandard { t.Fatalf("IFD name not correct.") @@ -64,25 +70,25 @@ func TestAdd(t *testing.T) { if tags[0].tagId != 0x11 { t.Fatalf("tag (0) tag-ID not correct") - } else if tags[0].valueBytes != "test string" { + } else if bytes.Compare(tags[0].value.([]byte), []byte("test string")) != 0 { t.Fatalf("tag (0) value not correct") } if tags[1].tagId != 0x22 { t.Fatalf("tag (1) tag-ID not correct") - } else if tags[1].valueBytes != "test string2" { + } else if bytes.Compare(tags[1].value.([]byte), []byte("test string2")) != 0 { t.Fatalf("tag (1) value not correct") } if tags[2].tagId != 0x33 { t.Fatalf("tag (2) tag-ID not correct") - } else if tags[2].valueBytes != "test string3" { + } else if bytes.Compare(tags[2].value.([]byte), []byte("test string3")) != 0 { t.Fatalf("tag (2) value not correct") } if tags[3].tagId != 0x44 { t.Fatalf("tag (3) tag-ID not correct") - } else if reflect.DeepEqual(tags[3].valueBytes.([]uint16), originalShorts) != true { + } else if bytes.Compare(tags[3].value.([]byte), originalBytes) != 0 { t.Fatalf("tag (3) value not correct") } } @@ -105,33 +111,35 @@ func TestSetNextIfd(t *testing.T) { } } -func TestAddChildIfd(t *testing.T) { +func TestAddChildIb(t *testing.T) { ib := NewIfdBuilder(IfdStandard, binary.BigEndian) bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) ibChild := NewIfdBuilder(IfdExif, binary.BigEndian) - err := ib.AddChildIfd(ibChild) + err = ib.AddChildIb(ibChild) log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) if ib.tags[0].tagId != 0x11 { t.Fatalf("first tag not correct") } else if ib.tags[1].tagId != ibChild.ifdTagId { - t.Fatalf("second tag ID does not match child-IFD tag-ID") - } else if ib.tags[1].valueBytes != ibChild { + t.Fatalf("second tag ID does not match child-IFD tag-ID: (0x%02x) != (0x%02x)", ib.tags[1].tagId, ibChild.ifdTagId) + } else if ib.tags[1].value != ibChild { t.Fatalf("second tagvalue does not match child-IFD") } else if ib.tags[2].tagId != 0x22 { t.Fatalf("third tag not correct") @@ -241,24 +249,27 @@ func TestFindN_First_1(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) found, err := ib.FindN(0x11, 1) log.PanicIf(err) @@ -282,24 +293,27 @@ func TestFindN_First_2_1Returned(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) found, err := ib.FindN(0x11, 2) log.PanicIf(err) @@ -323,38 +337,43 @@ func TestFindN_First_2_2Returned(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string5", + value: []byte("test string5"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) found, err := ib.FindN(0x11, 2) log.PanicIf(err) @@ -370,13 +389,13 @@ func TestFindN_First_2_2Returned(t *testing.T) { tags := ib.Tags() bt = tags[found[0]] - if bt.tagId != 0x11 || bt.valueBytes != "test string" { - log.Panicf("Found entry 0 is not correct: (0x%02x) [%s]", bt.tagId, bt.valueBytes) + if bt.tagId != 0x11 || bytes.Compare(bt.value.([]byte), []byte("test string")) != 0 { + log.Panicf("Found entry 0 is not correct: (0x%02x) [%s]", bt.tagId, bt.value) } bt = tags[found[1]] - if bt.tagId != 0x11 || bt.valueBytes != "test string4" { - log.Panicf("Found entry 1 is not correct: (0x%02x) [%s]", bt.tagId, bt.valueBytes) + if bt.tagId != 0x11 || bytes.Compare(bt.value.([]byte), []byte("test string4")) != 0 { + log.Panicf("Found entry 1 is not correct: (0x%02x) [%s]", bt.tagId, bt.value) } } @@ -385,45 +404,51 @@ func TestFindN_Middle_WithDuplicates(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string5", + value: []byte("test string5"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string6", + value: []byte("test string6"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) found, err := ib.FindN(0x33, 1) log.PanicIf(err) @@ -447,31 +472,35 @@ func TestFindN_Middle_NoDuplicates(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) found, err := ib.FindN(0x33, 1) log.PanicIf(err) @@ -506,31 +535,35 @@ func TestFind_Hit(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) position, err := ib.Find(0x33) log.PanicIf(err) @@ -552,33 +585,37 @@ func TestFind_Miss(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x11, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) - _, err := ib.Find(0x99) + _, err = ib.Find(0x99) if err == nil { t.Fatalf("Expected an error.") } else if log.Is(err, ErrTagEntryNotFound) == false { @@ -591,24 +628,27 @@ func TestReplace(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) currentIds := make([]uint16, 3) for i, bt := range ib.Tags() { @@ -621,10 +661,10 @@ func TestReplace(t *testing.T) { bt = builderTag{ tagId: 0x99, - valueBytes: "test string4", + value: []byte("test string4"), } - err := ib.Replace(0x22, bt) + err = ib.Replace(0x22, bt) log.PanicIf(err) currentIds = make([]uint16, 3) @@ -642,24 +682,27 @@ func TestReplaceN(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) currentIds := make([]uint16, 3) for i, bt := range ib.Tags() { @@ -672,10 +715,10 @@ func TestReplaceN(t *testing.T) { bt = builderTag{ tagId: 0xA9, - valueBytes: "test string4", + value: []byte("test string4"), } - err := ib.ReplaceAt(1, bt) + err = ib.ReplaceAt(1, bt) log.PanicIf(err) currentIds = make([]uint16, 3) @@ -693,31 +736,35 @@ func TestDeleteFirst(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) if len(ib.Tags()) != 4 { @@ -734,7 +781,7 @@ func TestDeleteFirst(t *testing.T) { } - err := ib.DeleteFirst(0x22) + err = ib.DeleteFirst(0x22) log.PanicIf(err) if len(ib.Tags()) != 3 { @@ -781,31 +828,35 @@ func TestDeleteN(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) if len(ib.Tags()) != 4 { @@ -822,7 +873,7 @@ func TestDeleteN(t *testing.T) { } - err := ib.DeleteN(0x22, 1) + err = ib.DeleteN(0x22, 1) log.PanicIf(err) if len(ib.Tags()) != 3 { @@ -869,31 +920,35 @@ func TestDeleteN_Two(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) if len(ib.Tags()) != 4 { @@ -910,7 +965,7 @@ func TestDeleteN_Two(t *testing.T) { } - err := ib.DeleteN(0x22, 2) + err = ib.DeleteN(0x22, 2) log.PanicIf(err) if len(ib.Tags()) != 2 { @@ -940,31 +995,35 @@ func TestDeleteAll(t *testing.T) { bt := builderTag{ tagId: 0x11, - valueBytes: "test string", + value: []byte("test string"), } - ib.Add(bt) + err := ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string2", + value: []byte("test string2"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x22, - valueBytes: "test string3", + value: []byte("test string3"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) bt = builderTag{ tagId: 0x33, - valueBytes: "test string4", + value: []byte("test string4"), } - ib.Add(bt) + err = ib.Add(bt) + log.PanicIf(err) if len(ib.Tags()) != 4 { @@ -1007,3 +1066,87 @@ func TestDeleteAll(t *testing.T) { log.Panic(err) } } + +func TestNewIfdBuilderFromExistingChain(t *testing.T) { + defer func() { + if state := recover(); state != nil { + err := log.Wrap(state.(error)) + log.PrintErrorf(err, "Test failure.") + } + }() + + e := NewExif() + + filepath := path.Join(assetsPath, "NDM_8901.jpg") + + exifData, err := e.SearchAndExtractExif(filepath) + log.PanicIf(err) + + _, index, err := e.Collect(exifData) + log.PanicIf(err) + + ib := NewIfdBuilderFromExistingChain(index.RootIfd, exifData) + lines := ib.DumpToStrings() + + expected := []string { + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(0) TAG=[0x10f]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(1) TAG=[0x110]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(2) TAG=[0x112]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(3) TAG=[0x11a]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(4) TAG=[0x11b]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(5) TAG=[0x128]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(6) TAG=[0x132]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(7) TAG=[0x13b]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(8) TAG=[0x213]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[] INDEX=(9) TAG=[0x8298]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[Exif] INDEX=(10) TAG=[0x8769]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(0) TAG=[0x829a]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(1) TAG=[0x829d]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(2) TAG=[0x8822]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(3) TAG=[0x8827]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(4) TAG=[0x8830]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(5) TAG=[0x8832]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(6) TAG=[0x9000]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(7) TAG=[0x9003]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(8) TAG=[0x9004]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(9) TAG=[0x9101]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(10) TAG=[0x9201]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(11) TAG=[0x9202]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(12) TAG=[0x9204]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(13) TAG=[0x9207]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(14) TAG=[0x9209]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(15) TAG=[0x920a]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(16) TAG=[0x927c]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(17) TAG=[0x9286]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(18) TAG=[0x9290]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(19) TAG=[0x9291]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(20) TAG=[0x9292]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(21) TAG=[0xa000]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(22) TAG=[0xa001]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(23) TAG=[0xa002]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(24) TAG=[0xa003]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(25) TAG=[0xa20e]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(26) TAG=[0xa20f]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(27) TAG=[0xa210]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(28) TAG=[0xa401]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(29) TAG=[0xa402]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(30) TAG=[0xa403]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(31) TAG=[0xa406]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(32) TAG=[0xa430]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(33) TAG=[0xa431]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(34) TAG=[0xa432]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(35) TAG=[0xa434]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[] INDEX=(36) TAG=[0xa435]", + " IFD-TAG-ID=(0x8769) CHILD-IFD=[Iop] INDEX=(37) TAG=[0xa005]", + "Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(0) TAG=[0x01]", + "Exif] IFD-NAME=[Iop]> IFD-TAG-ID=(0xa005) CHILD-IFD=[] INDEX=(1) TAG=[0x02]", + " IFD-TAG-ID=(0x00) CHILD-IFD=[GPSInfo] INDEX=(11) TAG=[0x8825]", + " IFD-TAG-ID=(0x8825) CHILD-IFD=[] INDEX=(0) TAG=[0x00]", + } + + if reflect.DeepEqual(lines, expected) == false { + t.Fatalf("IB did not [correctly] duplicate the IFD structure") + } +} + +// TODO(dustin): !! Test with a GPS-attached image. diff --git a/ifd_enumerate.go b/ifd_enumerate.go index a9397f5..68a0ee2 100644 --- a/ifd_enumerate.go +++ b/ifd_enumerate.go @@ -124,112 +124,12 @@ func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerato return ite } + // TagVisitor is an optional callback that can get hit for every tag we parse // through. `addressableData` is the byte array startign after the EXIF header // (where the offsets of all IFDs and values are calculated from). type TagVisitor func(indexedIfdName string, tagId uint16, tagType TagType, valueContext ValueContext) (err error) - -type IfdTagEntry struct { - TagId uint16 - TagIndex int - TagType uint16 - UnitCount uint32 - ValueOffset uint32 - RawValueOffset []byte - - // ChildIfdName is a name if this tag represents a child IFD. - ChildIfdName string - - // IfdName is the IFD that this tag belongs to. - IfdName string -} - -func (ite IfdTagEntry) String() string { - return fmt.Sprintf("IfdTagEntry", ite.ChildIfdName, ite.TagId, TypeNames[ite.TagType], ite.UnitCount) -} - -func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() - - if ite.TagType == TypeUndefined { - valueContext := ValueContext{ - UnitCount: ite.UnitCount, - ValueOffset: ite.ValueOffset, - RawValueOffset: ite.RawValueOffset, - AddressableData: addressableData, - } - - value, err := UndefinedValue(ite.IfdName, ite.TagId, valueContext, byteOrder) - log.PanicIf(err) - - switch value.(type) { - case []byte: - return value.([]byte), nil - case string: - return []byte(value.(string)), nil - default: -// TODO(dustin): !! Finish translating the rest of the types (make reusable and replace into other similar implementations?) - log.Panicf("can not produce bytes for unknown-type tag (0x%02x)", ite.TagId) - } - } - - originalType := NewTagType(ite.TagType, byteOrder) - byteCount := uint32(originalType.Size()) * ite.UnitCount - - tt := NewTagType(TypeByte, byteOrder) - - if tt.ValueIsEmbedded(byteCount) == true { - typeDecodeLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).") - - // In this case, the bytes normally used for the offset are actually - // data. - value, err = tt.ParseBytes(ite.RawValueOffset, byteCount) - log.PanicIf(err) - } else { - typeDecodeLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).") - - value, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount) - log.PanicIf(err) - } - - return value, nil -} - - -type IfdTagEntryValueResolver struct { - addressableData []byte - byteOrder binary.ByteOrder -} - -func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (itevr *IfdTagEntryValueResolver) { - // Make it obvious what data we expect and when we don't get it. - if IsExif(exifData) == false { - log.Panicf("not exif data") - } - - return &IfdTagEntryValueResolver{ - addressableData: exifData[ExifAddressableAreaStart:], - byteOrder: byteOrder, - } -} - -func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() - - value, err = ite.ValueBytes(itevr.addressableData, itevr.byteOrder) - return value, err -} - - // ParseIfd decodes the IFD block that we're currently sitting on the first // byte of. func (ie *IfdEnumerate) ParseIfd(ifdName string, ifdIndex int, ifdOffset uint32, visitor TagVisitor, doDescend bool) (nextIfdOffset uint32, entries []IfdTagEntry, err error) { @@ -347,6 +247,8 @@ func (ie *IfdEnumerate) Scan(ifdName string, ifdOffset uint32, visitor TagVisito type Ifd struct { + ByteOrder binary.ByteOrder + Id int ParentIfd *Ifd Name string @@ -450,6 +352,7 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error id := len(ifds) ifd := Ifd{ + ByteOrder: ie.byteOrder, Id: id, ParentIfd: parentIfd, Name: name, diff --git a/ifd_tag_entry.go b/ifd_tag_entry.go new file mode 100644 index 0000000..f774944 --- /dev/null +++ b/ifd_tag_entry.go @@ -0,0 +1,120 @@ +package exif + +import ( + "fmt" + "reflect" + + "encoding/binary" + + "github.com/dsoprea/go-logging" +) + +var ( + iteLogger = log.NewLogger("exif.ifd_tag_entry") +) + +type IfdTagEntry struct { + TagId uint16 + TagIndex int + TagType uint16 + UnitCount uint32 + ValueOffset uint32 + RawValueOffset []byte + + // ChildIfdName is a name if this tag represents a child IFD. + ChildIfdName string + + // IfdName is the IFD that this tag belongs to. + IfdName string +} + +func (ite IfdTagEntry) String() string { + return fmt.Sprintf("IfdTagEntry", ite.ChildIfdName, ite.TagId, TypeNames[ite.TagType], ite.UnitCount) +} + +func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + if ite.TagType == TypeUndefined { + valueContext := ValueContext{ + UnitCount: ite.UnitCount, + ValueOffset: ite.ValueOffset, + RawValueOffset: ite.RawValueOffset, + AddressableData: addressableData, + } + + value, err := UndefinedValue(ite.IfdName, ite.TagId, valueContext, byteOrder) + log.PanicIf(err) + + switch value.(type) { + case []byte: + return value.([]byte), nil + case string: + return []byte(value.(string)), nil + case UnknownTagValue: + valueBytes, err := value.(UnknownTagValue).ValueBytes() + log.PanicIf(err) + + return valueBytes, nil + default: +// TODO(dustin): !! Finish translating the rest of the types (make reusable and replace into other similar implementations?) + log.Panicf("can not produce bytes for unknown-type tag (0x%02x): [%s]", ite.TagId, reflect.TypeOf(value)) + } + } + + originalType := NewTagType(ite.TagType, byteOrder) + byteCount := uint32(originalType.Size()) * ite.UnitCount + + tt := NewTagType(TypeByte, byteOrder) + + if tt.ValueIsEmbedded(byteCount) == true { + iteLogger.Debugf(nil, "Reading BYTE value (ITE; embedded).") + + // In this case, the bytes normally used for the offset are actually + // data. + value, err = tt.ParseBytes(ite.RawValueOffset, byteCount) + log.PanicIf(err) + } else { + iteLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).") + + value, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount) + log.PanicIf(err) + } + + return value, nil +} + + +// IfdTagEntryValueResolver instances know how to resolve the values for any +// tag for a particular EXIF block. +type IfdTagEntryValueResolver struct { + addressableData []byte + byteOrder binary.ByteOrder +} + +func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (itevr *IfdTagEntryValueResolver) { + // Make it obvious what data we expect and when we don't get it. + if IsExif(exifData) == false { + log.Panicf("not exif data") + } + + return &IfdTagEntryValueResolver{ + addressableData: exifData[ExifAddressableAreaStart:], + byteOrder: byteOrder, + } +} + +func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + value, err = ite.ValueBytes(itevr.addressableData, itevr.byteOrder) + return value, err +} diff --git a/tags.go b/tags.go index 5d8a634..d537a02 100644 --- a/tags.go +++ b/tags.go @@ -181,9 +181,9 @@ func (ti *TagIndex) Get(ifdName string, id uint16) (it *IndexedTag, err error) { return it, nil } -// GetIfdName returns the known index name for the tags that are expected/ -// allowed for the IFD. If there's an error, returns "". If returns "", the IFD -// should be skipped. +// IfdName returns the known index name for the tags that are expected/allowed +// for the IFD. If there's an error, returns "". If returns "", the IFD should +// be skipped. func IfdName(ifdName string, ifdIndex int) string { // There's an IFD0 and IFD1, but the others must be unique. if ifdName == IfdStandard && ifdIndex > 1 { diff --git a/tags_unknown.go b/tags_unknown.go index 4702b04..d996b82 100644 --- a/tags_unknown.go +++ b/tags_unknown.go @@ -3,6 +3,8 @@ package exif import ( "fmt" "strings" + + "github.com/dsoprea/go-logging" ) const ( @@ -69,6 +71,19 @@ var ( } ) + +type UnknownTagValue interface { + ValueBytes() ([]byte, error) +} + + +type TagUnknownType_GeneralString string + +func (gs TagUnknownType_GeneralString) ValueBytes() (value []byte, err error) { + return []byte(gs), nil +} + + type TagUnknownType_9298_UserComment struct { EncodingType int EncodingBytes []byte @@ -78,6 +93,21 @@ func (uc TagUnknownType_9298_UserComment) String() string { return fmt.Sprintf("UserComment", TagUnknownType_9298_UserComment_Encoding_Names[uc.EncodingType], uc.EncodingBytes) } +func (uc TagUnknownType_9298_UserComment) ValueBytes() (value []byte, err error) { + encodingTypeBytes, found := TagUnknownType_9298_UserComment_Encodings[uc.EncodingType] + if found == false { + log.Panicf("encoding-type not valid for unknown-type tag 9298 (UserComment): (%d)", uc.EncodingType) + } + + value = make([]byte, len(uc.EncodingBytes) + 8) + copy(value[:8], encodingTypeBytes) + +// TODO(dustin): !! With undefined-encoded comments, we always make this empty. However, it comes in with a set of zero bytes. Is there a problem if we send it out with just the encoding bytes? + copy(value[8:], uc.EncodingBytes) + + return value, nil +} + type TagUnknownType_927C_MakerNote struct { MakerNoteType []byte @@ -93,6 +123,10 @@ func (mn TagUnknownType_927C_MakerNote) String() string { return fmt.Sprintf("MakerNote", strings.Join(parts, " ")) } +func (uc TagUnknownType_927C_MakerNote) ValueBytes() (value []byte, err error) { + return uc.MakerNoteBytes, nil +} + type TagUnknownType_9101_ComponentsConfiguration struct { ConfigurationId int @@ -102,3 +136,7 @@ type TagUnknownType_9101_ComponentsConfiguration struct { func (cc TagUnknownType_9101_ComponentsConfiguration) String() string { return fmt.Sprintf("ComponentsConfiguration", TagUnknownType_9101_ComponentsConfiguration_Names[cc.ConfigurationId], cc.ConfigurationBytes) } + +func (uc TagUnknownType_9101_ComponentsConfiguration) ValueBytes() (value []byte, err error) { + return uc.ConfigurationBytes, nil +} diff --git a/type.go b/type.go index 4e9c60f..c8f75a7 100644 --- a/type.go +++ b/type.go @@ -36,10 +36,6 @@ var ( ) var ( - // ErrCantDetermineTagValueSize is used when we're trying to determine a - //size for a non-standard/undefined type. - ErrCantDetermineTagValueSize = errors.New("can not determine tag-value size") - // ErrNotEnoughData is used when there isn't enough data to accomodate what // we're trying to parse (sizeof(type) * unit_count). ErrNotEnoughData = errors.New("not enough data for type") diff --git a/type_decode.go b/type_decode.go index b0fe7bd..80068a9 100644 --- a/type_decode.go +++ b/type_decode.go @@ -66,7 +66,7 @@ func (tt TagType) Size() int { } else if tt.tagType == TypeSignedRational { return 8 } else { - log.Panic(ErrCantDetermineTagValueSize) + log.Panicf("can not determine tag-value size for type (%d): [%s]", tt.tagType, TypeNames[tt.tagType]) // Never called. return 0 @@ -609,19 +609,19 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte tt := NewTagType(TypeAsciiNoNul, byteOrder) - value, err = tt.ReadAsciiValue(valueContext) + valueString, err := tt.ReadAsciiValue(valueContext) log.PanicIf(err) - return value, nil + return TagUnknownType_GeneralString(valueString), nil } else if tagId == 0xa000 { // FlashpixVersion tt := NewTagType(TypeAsciiNoNul, byteOrder) - value, err = tt.ReadAsciiValue(valueContext) + valueString, err := tt.ReadAsciiValue(valueContext) log.PanicIf(err) - return value, nil + return TagUnknownType_GeneralString(valueString), nil } else if tagId == 0x9286 { // UserComment @@ -719,19 +719,19 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte tt := NewTagType(TypeAsciiNoNul, byteOrder) - value, err = tt.ReadAsciiValue(valueContext) + valueString, err := tt.ReadAsciiValue(valueContext) log.PanicIf(err) - return value, nil + return TagUnknownType_GeneralString(valueString), nil } else if tagId == 0x001b { // GPSProcessingMethod tt := NewTagType(TypeAsciiNoNul, byteOrder) - value, err = tt.ReadAsciiValue(valueContext) + valueString, err := tt.ReadAsciiValue(valueContext) log.PanicIf(err) - return value, nil + return TagUnknownType_GeneralString(valueString), nil } } else if indexedIfdName == IfdName(IfdIop, 0) { if tagId == 0x0002 { @@ -739,10 +739,10 @@ func UndefinedValue(indexedIfdName string, tagId uint16, valueContext ValueConte tt := NewTagType(TypeAsciiNoNul, byteOrder) - value, err := tt.ReadAsciiNoNulValue(valueContext) + valueString, err := tt.ReadAsciiValue(valueContext) log.PanicIf(err) - return value, nil + return TagUnknownType_GeneralString(valueString), nil } }