mirror of https://github.com/dsoprea/go-exif.git
tags.go: 'Get' functions now take an exifcommon.IfdIdentity
parent
1a12aec48f
commit
75b5552112
|
@ -63,6 +63,7 @@ func TestVisit(t *testing.T) {
|
|||
|
||||
tags := make([]string, 0)
|
||||
|
||||
// DEPRECATED(dustin): fqIfdPath and ifdIndex are now redundant. Remove in next module version.
|
||||
visitor := func(fqIfdPath string, ifdIndex int, ite *IfdTagEntry) (err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
|
@ -73,14 +74,12 @@ func TestVisit(t *testing.T) {
|
|||
|
||||
tagId := ite.TagId()
|
||||
tagType := ite.TagType()
|
||||
ii := ite.ifdIdentity
|
||||
|
||||
ifdPath, err := im.StripPathPhraseIndices(fqIfdPath)
|
||||
log.PanicIf(err)
|
||||
|
||||
it, err := ti.Get(ifdPath, tagId)
|
||||
it, err := ti.Get(ii, tagId)
|
||||
if err != nil {
|
||||
if log.Is(err, ErrTagNotFound) {
|
||||
fmt.Printf("Unknown tag: [%s] (%04x)\n", ifdPath, tagId)
|
||||
fmt.Printf("Unknown tag: [%s] (%04x)\n", ii.String(), tagId)
|
||||
return nil
|
||||
} else {
|
||||
log.Panic(err)
|
||||
|
@ -90,7 +89,7 @@ func TestVisit(t *testing.T) {
|
|||
valueString, err := ite.FormatFirst()
|
||||
log.PanicIf(err)
|
||||
|
||||
description := fmt.Sprintf("IFD-PATH=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]", ifdPath, tagId, it.Name, ite.UnitCount(), tagType.String(), valueString)
|
||||
description := fmt.Sprintf("IFD-PATH=[%s] ID=(0x%04x) NAME=[%s] COUNT=(%d) TYPE=[%s] VALUE=[%s]", ii.String(), tagId, it.Name, ite.UnitCount(), tagType.String(), valueString)
|
||||
tags = append(tags, description)
|
||||
|
||||
return nil
|
||||
|
@ -157,12 +156,12 @@ func TestVisit(t *testing.T) {
|
|||
"IFD-PATH=[IFD/Exif] ID=(0xa435) NAME=[LensSerialNumber] COUNT=(11) TYPE=[ASCII] VALUE=[2400001068]",
|
||||
"IFD-PATH=[IFD] ID=(0x8825) NAME=[GPSTag] COUNT=(1) TYPE=[LONG] VALUE=[9554]",
|
||||
"IFD-PATH=[IFD/GPSInfo] ID=(0x0000) NAME=[GPSVersionID] COUNT=(4) TYPE=[BYTE] VALUE=[02 03 00 00]",
|
||||
"IFD-PATH=[IFD] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6]",
|
||||
"IFD-PATH=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||
"IFD-PATH=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||
"IFD-PATH=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||
"IFD-PATH=[IFD] ID=(0x0201) NAME=[JPEGInterchangeFormat] COUNT=(1) TYPE=[LONG] VALUE=[11444]",
|
||||
"IFD-PATH=[IFD] ID=(0x0202) NAME=[JPEGInterchangeFormatLength] COUNT=(1) TYPE=[LONG] VALUE=[21491]",
|
||||
"IFD-PATH=[IFD1] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[6]",
|
||||
"IFD-PATH=[IFD1] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||
"IFD-PATH=[IFD1] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[72/1]",
|
||||
"IFD-PATH=[IFD1] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[2]",
|
||||
"IFD-PATH=[IFD1] ID=(0x0201) NAME=[JPEGInterchangeFormat] COUNT=(1) TYPE=[LONG] VALUE=[11444]",
|
||||
"IFD-PATH=[IFD1] ID=(0x0202) NAME=[JPEGInterchangeFormatLength] COUNT=(1) TYPE=[LONG] VALUE=[21491]",
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(tags, expected) == false {
|
||||
|
|
|
@ -516,7 +516,7 @@ func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
|
|||
err = ib.Set(offsetBt)
|
||||
log.PanicIf(err)
|
||||
|
||||
thumbnailSizeIt, err := ib.tagIndex.Get(ib.IfdIdentity().UnindexedString(), ThumbnailSizeTagId)
|
||||
thumbnailSizeIt, err := ib.tagIndex.Get(ib.IfdIdentity(), ThumbnailSizeTagId)
|
||||
log.PanicIf(err)
|
||||
|
||||
sizeBt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), thumbnailSizeIt, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))})
|
||||
|
@ -565,7 +565,7 @@ func (ib *IfdBuilder) printTagTree(levels int) {
|
|||
if isChildIb == true {
|
||||
tagName = "<Child IFD>"
|
||||
} else {
|
||||
it, err := ib.tagIndex.Get(tag.ifdPath, tag.tagId)
|
||||
it, err := ib.tagIndex.Get(ib.ifdIdentity, tag.tagId)
|
||||
if log.Is(err, ErrTagNotFound) == true {
|
||||
tagName = "<UNKNOWN>"
|
||||
} else if err != nil {
|
||||
|
@ -889,7 +889,7 @@ func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error
|
|||
}
|
||||
}()
|
||||
|
||||
it, err := ib.tagIndex.GetWithName(ib.IfdIdentity().UnindexedString(), tagName)
|
||||
it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName)
|
||||
log.PanicIf(err)
|
||||
|
||||
found, err := ib.FindN(it.Id, 1)
|
||||
|
@ -1105,7 +1105,7 @@ func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
it, err := ib.tagIndex.Get(ib.IfdIdentity().UnindexedString(), tagId)
|
||||
it, err := ib.tagIndex.Get(ib.IfdIdentity(), tagId)
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
|
||||
|
@ -1126,7 +1126,7 @@ func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (er
|
|||
}
|
||||
}()
|
||||
|
||||
it, err := ib.tagIndex.GetWithName(ib.IfdIdentity().UnindexedString(), tagName)
|
||||
it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName)
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
|
||||
|
@ -1148,7 +1148,7 @@ func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error) {
|
|||
|
||||
// TODO(dustin): !! Add test for this function.
|
||||
|
||||
it, err := ib.tagIndex.Get(ib.IfdIdentity().UnindexedString(), tagId)
|
||||
it, err := ib.tagIndex.Get(ib.IfdIdentity(), tagId)
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
|
||||
|
@ -1179,7 +1179,7 @@ func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (er
|
|||
|
||||
// TODO(dustin): !! Add test for this function.
|
||||
|
||||
it, err := ib.tagIndex.GetWithName(ib.IfdIdentity().UnindexedString(), tagName)
|
||||
it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName)
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value)
|
||||
|
|
|
@ -198,7 +198,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded1(t *testing.T) {
|
|||
ti := NewTagIndex()
|
||||
ib := NewIfdBuilder(im, ti, exifcommon.IfdGpsInfoStandardIfdIdentity, exifcommon.TestDefaultByteOrder)
|
||||
|
||||
it, err := ti.Get(ib.IfdIdentity().UnindexedString(), uint16(0x0000))
|
||||
it, err := ti.Get(ib.IfdIdentity(), uint16(0x0000))
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString(), it, exifcommon.TestDefaultByteOrder, []uint8{uint8(0x12)})
|
||||
|
@ -232,7 +232,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_embedded2(t *testing.T) {
|
|||
ti := NewTagIndex()
|
||||
ib := NewIfdBuilder(im, ti, exifcommon.IfdGpsInfoStandardIfdIdentity, exifcommon.TestDefaultByteOrder)
|
||||
|
||||
it, err := ti.Get(ib.IfdIdentity().UnindexedString(), uint16(0x0000))
|
||||
it, err := ti.Get(ib.IfdIdentity(), uint16(0x0000))
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString(), it, exifcommon.TestDefaultByteOrder, []uint8{uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78)})
|
||||
|
@ -272,7 +272,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_bytes_allocated(t *testing.T) {
|
|||
addressableOffset := uint32(0x1234)
|
||||
ida := newIfdDataAllocator(addressableOffset)
|
||||
|
||||
it, err := ti.Get(ib.IfdIdentity().UnindexedString(), uint16(0x0000))
|
||||
it, err := ti.Get(ib.IfdIdentity(), uint16(0x0000))
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString(), it, exifcommon.TestDefaultByteOrder, []uint8{uint8(0x12), uint8(0x34), uint8(0x56), uint8(0x78), uint8(0x9a)})
|
||||
|
@ -493,7 +493,7 @@ func Test_IfdByteEncoder_encodeTagToBytes_simpleTag_allocate(t *testing.T) {
|
|||
ti := NewTagIndex()
|
||||
ib := NewIfdBuilder(im, ti, exifcommon.IfdStandardIfdIdentity, exifcommon.TestDefaultByteOrder)
|
||||
|
||||
it, err := ib.tagIndex.Get(ib.IfdIdentity().UnindexedString(), uint16(0x000b))
|
||||
it, err := ib.tagIndex.Get(ib.IfdIdentity(), uint16(0x000b))
|
||||
log.PanicIf(err)
|
||||
|
||||
valueString := "testvalue"
|
||||
|
|
|
@ -2049,7 +2049,7 @@ func TestIfdBuilder_CreateIfdBuilderWithExistingIfd(t *testing.T) {
|
|||
func TestNewStandardBuilderTag__OneUnit(t *testing.T) {
|
||||
ti := NewTagIndex()
|
||||
|
||||
it, err := ti.Get(exifcommon.IfdExifStandardIfdIdentity.UnindexedString(), uint16(0x8833))
|
||||
it, err := ti.Get(exifcommon.IfdExifStandardIfdIdentity, uint16(0x8833))
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(exifcommon.IfdExifStandardIfdIdentity.UnindexedString(), it, exifcommon.TestDefaultByteOrder, []uint32{uint32(0x1234)})
|
||||
|
@ -2066,7 +2066,7 @@ func TestNewStandardBuilderTag__OneUnit(t *testing.T) {
|
|||
func TestNewStandardBuilderTag__TwoUnits(t *testing.T) {
|
||||
ti := NewTagIndex()
|
||||
|
||||
it, err := ti.Get(exifcommon.IfdExifStandardIfdIdentity.UnindexedString(), uint16(0x8833))
|
||||
it, err := ti.Get(exifcommon.IfdExifStandardIfdIdentity, uint16(0x8833))
|
||||
log.PanicIf(err)
|
||||
|
||||
bt := NewStandardBuilderTag(exifcommon.IfdExifStandardIfdIdentity.UnindexedString(), it, exifcommon.TestDefaultByteOrder, []uint32{uint32(0x1234), uint32(0x5678)})
|
||||
|
|
|
@ -312,7 +312,7 @@ func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, vis
|
|||
tagId := ite.TagId()
|
||||
tagType := ite.TagType()
|
||||
|
||||
it, err := ie.tagIndex.Get(ii.UnindexedString(), tagId)
|
||||
it, err := ie.tagIndex.Get(ii, tagId)
|
||||
if err == nil {
|
||||
// This is a known tag (from the standard, unless the user did
|
||||
// something different).
|
||||
|
@ -609,7 +609,7 @@ func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err err
|
|||
}
|
||||
}()
|
||||
|
||||
it, err := ifd.tagIndex.GetWithName(ifd.ifdIdentity.UnindexedString(), tagName)
|
||||
it, err := ifd.tagIndex.GetWithName(ifd.ifdIdentity, tagName)
|
||||
if log.Is(err, ErrTagNotFound) == true {
|
||||
log.Panic(ErrTagNotStandard)
|
||||
} else if err != nil {
|
||||
|
@ -717,7 +717,7 @@ func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink boo
|
|||
continue
|
||||
}
|
||||
|
||||
it, err := ifd.tagIndex.Get(ifd.ifdIdentity.UnindexedString(), ite.TagId())
|
||||
it, err := ifd.tagIndex.Get(ifd.ifdIdentity, ite.TagId())
|
||||
|
||||
tagName := ""
|
||||
if err == nil {
|
||||
|
|
|
@ -38,8 +38,7 @@ type IfdTagEntry struct {
|
|||
|
||||
// TODO(dustin): !! IB's host the child-IBs directly in the tag, but that's not the case here. Refactor to accomodate it for a consistent experience.
|
||||
|
||||
// fqIfdPath is the IFD that this tag belongs to.
|
||||
fqIfdPath string
|
||||
ifdIdentity *exifcommon.IfdIdentity
|
||||
|
||||
isUnhandledUnknown bool
|
||||
|
||||
|
@ -49,7 +48,7 @@ type IfdTagEntry struct {
|
|||
|
||||
func newIfdTagEntry(ii *exifcommon.IfdIdentity, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, addressableData []byte, byteOrder binary.ByteOrder) *IfdTagEntry {
|
||||
return &IfdTagEntry{
|
||||
fqIfdPath: ii.String(),
|
||||
ifdIdentity: ii,
|
||||
tagId: tagId,
|
||||
tagIndex: tagIndex,
|
||||
tagType: tagType,
|
||||
|
@ -63,12 +62,12 @@ func newIfdTagEntry(ii *exifcommon.IfdIdentity, tagId uint16, tagIndex int, tagT
|
|||
|
||||
// String returns a stringified representation of the struct.
|
||||
func (ite *IfdTagEntry) String() string {
|
||||
return fmt.Sprintf("IfdTagEntry<TAG-IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.fqIfdPath, ite.tagId, ite.tagType.String(), ite.unitCount)
|
||||
return fmt.Sprintf("IfdTagEntry<TAG-IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.ifdIdentity.String(), ite.tagId, ite.tagType.String(), ite.unitCount)
|
||||
}
|
||||
|
||||
// IfdPath returns the fully-qualified path of the IFD that owns this tag.
|
||||
func (ite *IfdTagEntry) IfdPath() string {
|
||||
return ite.fqIfdPath
|
||||
return ite.ifdIdentity.String()
|
||||
}
|
||||
|
||||
// TagId returns the ID of the tag that we represent. The combination of
|
||||
|
@ -80,13 +79,13 @@ func (ite *IfdTagEntry) TagId() uint16 {
|
|||
// IsThumbnailOffset returns true if the tag has the IFD and tag-ID of a
|
||||
// thumbnail offset.
|
||||
func (ite *IfdTagEntry) IsThumbnailOffset() bool {
|
||||
return ite.tagId == ThumbnailOffsetTagId && ite.fqIfdPath == ThumbnailFqIfdPath
|
||||
return ite.tagId == ThumbnailOffsetTagId && ite.ifdIdentity.String() == ThumbnailFqIfdPath
|
||||
}
|
||||
|
||||
// IsThumbnailSize returns true if the tag has the IFD and tag-ID of a thumbnail
|
||||
// size.
|
||||
func (ite *IfdTagEntry) IsThumbnailSize() bool {
|
||||
return ite.tagId == ThumbnailSizeTagId && ite.fqIfdPath == ThumbnailFqIfdPath
|
||||
return ite.tagId == ThumbnailSizeTagId && ite.ifdIdentity.String() == ThumbnailFqIfdPath
|
||||
}
|
||||
|
||||
// TagType is the type of value for this tag.
|
||||
|
@ -256,7 +255,7 @@ func (ite *IfdTagEntry) ChildFqIfdPath() string {
|
|||
|
||||
func (ite *IfdTagEntry) getValueContext() *exifcommon.ValueContext {
|
||||
return exifcommon.NewValueContext(
|
||||
ite.fqIfdPath,
|
||||
ite.ifdIdentity.String(),
|
||||
ite.tagId,
|
||||
ite.unitCount,
|
||||
ite.valueOffset,
|
||||
|
|
10
v2/tags.go
10
v2/tags.go
|
@ -111,6 +111,8 @@ func (it *IndexedTag) Is(ifdPath string, id uint16) bool {
|
|||
return it.Id == id && it.IfdPath == ifdPath
|
||||
}
|
||||
|
||||
// WidestSupportedType returns the largest type that this tag's value can
|
||||
// occupy
|
||||
func (it *IndexedTag) WidestSupportedType() exifcommon.TagTypePrimitive {
|
||||
if len(it.SupportedTypes) == 0 {
|
||||
log.Panicf("IndexedTag [%s] (%d) has no supported types.", it.IfdPath, it.Id)
|
||||
|
@ -208,7 +210,7 @@ func (ti *TagIndex) Add(it *IndexedTag) (err error) {
|
|||
|
||||
// Get returns information about the non-IFD tag given a tag ID. `ifdPath` must
|
||||
// not be fully-qualified.
|
||||
func (ti *TagIndex) Get(ifdPath string, id uint16) (it *IndexedTag, err error) {
|
||||
func (ti *TagIndex) Get(ii *exifcommon.IfdIdentity, id uint16) (it *IndexedTag, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
|
@ -220,6 +222,8 @@ func (ti *TagIndex) Get(ifdPath string, id uint16) (it *IndexedTag, err error) {
|
|||
log.PanicIf(err)
|
||||
}
|
||||
|
||||
ifdPath := ii.UnindexedString()
|
||||
|
||||
family, found := ti.tagsByIfd[ifdPath]
|
||||
if found == false {
|
||||
return nil, ErrTagNotFound
|
||||
|
@ -234,7 +238,7 @@ func (ti *TagIndex) Get(ifdPath string, id uint16) (it *IndexedTag, err error) {
|
|||
}
|
||||
|
||||
// GetWithName returns information about the non-IFD tag given a tag name.
|
||||
func (ti *TagIndex) GetWithName(ifdPath string, name string) (it *IndexedTag, err error) {
|
||||
func (ti *TagIndex) GetWithName(ii *exifcommon.IfdIdentity, name string) (it *IndexedTag, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
|
@ -246,6 +250,8 @@ func (ti *TagIndex) GetWithName(ifdPath string, name string) (it *IndexedTag, er
|
|||
log.PanicIf(err)
|
||||
}
|
||||
|
||||
ifdPath := ii.UnindexedString()
|
||||
|
||||
it, found := ti.tagsByIfdR[ifdPath][name]
|
||||
if found != true {
|
||||
log.Panic(ErrTagNotFound)
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
func TestGet(t *testing.T) {
|
||||
ti := NewTagIndex()
|
||||
|
||||
it, err := ti.Get(exifcommon.IfdStandardIfdIdentity.UnindexedString(), 0x10f)
|
||||
it, err := ti.Get(exifcommon.IfdStandardIfdIdentity, 0x10f)
|
||||
log.PanicIf(err)
|
||||
|
||||
if it.Is(exifcommon.IfdStandardIfdIdentity.UnindexedString(), 0x10f) == false || it.IsName(exifcommon.IfdStandardIfdIdentity.UnindexedString(), "Make") == false {
|
||||
|
@ -22,7 +22,7 @@ func TestGet(t *testing.T) {
|
|||
func TestGetWithName(t *testing.T) {
|
||||
ti := NewTagIndex()
|
||||
|
||||
it, err := ti.GetWithName(exifcommon.IfdStandardIfdIdentity.UnindexedString(), "Make")
|
||||
it, err := ti.GetWithName(exifcommon.IfdStandardIfdIdentity, "Make")
|
||||
log.PanicIf(err)
|
||||
|
||||
if it.Is(exifcommon.IfdStandardIfdIdentity.UnindexedString(), 0x10f) == false {
|
||||
|
|
|
@ -141,12 +141,9 @@ func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) {
|
|||
|
||||
visitor := func(fqIfdPath string, ifdIndex int, ite *IfdTagEntry) (err error) {
|
||||
tagId := ite.TagId()
|
||||
ii := ite.ifdIdentity
|
||||
|
||||
// TODO(dustin): This is inefficient. Our IFD paths should have their own type where we can render whatever path we need.
|
||||
ifdPath, err := im.StripPathPhraseIndices(fqIfdPath)
|
||||
log.PanicIf(err)
|
||||
|
||||
it, err := ti.Get(ifdPath, ite.tagId)
|
||||
it, err := ti.Get(ii, ite.tagId)
|
||||
if err != nil {
|
||||
if log.Is(err, ErrTagNotFound) != true {
|
||||
log.Panic(err)
|
||||
|
|
Loading…
Reference in New Issue