summaryrefslogtreecommitdiff
path: root/vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go
diff options
context:
space:
mode:
authorLibravatar Tobi Smethurst <31960611+tsmethurst@users.noreply.github.com>2021-08-12 21:03:24 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-12 21:03:24 +0200
commit98263a7de64269898a2f81207e38943b5c8e8653 (patch)
tree743c90f109a6c5d27832d1dcef2388d939f0f77a /vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go
parentText duplication fix (#137) (diff)
downloadgotosocial-98263a7de64269898a2f81207e38943b5c8e8653.tar.xz
Grand test fixup (#138)
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
Diffstat (limited to 'vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go')
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go395
1 files changed, 395 insertions, 0 deletions
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go
new file mode 100644
index 000000000..1b5b06a3a
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go
@@ -0,0 +1,395 @@
+package jpegstructure
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+
+ "crypto/sha1"
+ "encoding/binary"
+
+ "github.com/dsoprea/go-exif/v2"
+ "github.com/dsoprea/go-iptc"
+ "github.com/dsoprea/go-logging"
+)
+
+// SegmentList contains a slice of segments.
+type SegmentList struct {
+ segments []*Segment
+}
+
+// NewSegmentList returns a new SegmentList struct.
+func NewSegmentList(segments []*Segment) (sl *SegmentList) {
+ if segments == nil {
+ segments = make([]*Segment, 0)
+ }
+
+ return &SegmentList{
+ segments: segments,
+ }
+}
+
+// OffsetsEqual returns true is all segments have the same marker-IDs and were
+// found at the same offsets.
+func (sl *SegmentList) OffsetsEqual(o *SegmentList) bool {
+ if len(o.segments) != len(sl.segments) {
+ return false
+ }
+
+ for i, s := range o.segments {
+ if s.MarkerId != sl.segments[i].MarkerId || s.Offset != sl.segments[i].Offset {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Segments returns the underlying slice of segments.
+func (sl *SegmentList) Segments() []*Segment {
+ return sl.segments
+}
+
+// Add adds another segment.
+func (sl *SegmentList) Add(s *Segment) {
+ sl.segments = append(sl.segments, s)
+}
+
+// Print prints segment info.
+func (sl *SegmentList) Print() {
+ if len(sl.segments) == 0 {
+ fmt.Printf("No segments.\n")
+ } else {
+ exifIndex, _, err := sl.FindExif()
+ if err != nil {
+ if err == exif.ErrNoExif {
+ exifIndex = -1
+ } else {
+ log.Panic(err)
+ }
+ }
+
+ xmpIndex, _, err := sl.FindXmp()
+ if err != nil {
+ if err == ErrNoXmp {
+ xmpIndex = -1
+ } else {
+ log.Panic(err)
+ }
+ }
+
+ iptcIndex, _, err := sl.FindIptc()
+ if err != nil {
+ if err == ErrNoIptc {
+ iptcIndex = -1
+ } else {
+ log.Panic(err)
+ }
+ }
+
+ for i, s := range sl.segments {
+ fmt.Printf("%2d: %s", i, s.EmbeddedString())
+
+ if i == exifIndex {
+ fmt.Printf(" [EXIF]")
+ } else if i == xmpIndex {
+ fmt.Printf(" [XMP]")
+ } else if i == iptcIndex {
+ fmt.Printf(" [IPTC]")
+ }
+
+ fmt.Printf("\n")
+ }
+ }
+}
+
+// Validate checks that all of the markers are actually located at all of the
+// recorded offsets.
+func (sl *SegmentList) Validate(data []byte) (err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if len(sl.segments) < 2 {
+ log.Panicf("minimum segments not found")
+ }
+
+ if sl.segments[0].MarkerId != MARKER_SOI {
+ log.Panicf("first segment not SOI")
+ } else if sl.segments[len(sl.segments)-1].MarkerId != MARKER_EOI {
+ log.Panicf("last segment not EOI")
+ }
+
+ lastOffset := 0
+ for i, s := range sl.segments {
+ if lastOffset != 0 && s.Offset <= lastOffset {
+ log.Panicf("segment offset not greater than the last: SEGMENT=(%d) (0x%08x) <= (0x%08x)", i, s.Offset, lastOffset)
+ }
+
+ // The scan-data doesn't start with a marker.
+ if s.MarkerId == 0x0 {
+ continue
+ }
+
+ o := s.Offset
+ if bytes.Compare(data[o:o+2], []byte{0xff, s.MarkerId}) != 0 {
+ log.Panicf("segment offset does not point to the start of a segment: SEGMENT=(%d) (0x%08x)", i, s.Offset)
+ }
+
+ lastOffset = o
+ }
+
+ return nil
+}
+
+// FindExif returns the the segment that hosts the EXIF data (if present).
+func (sl *SegmentList) FindExif() (index int, segment *Segment, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ for i, s := range sl.segments {
+ if s.IsExif() == true {
+ return i, s, nil
+ }
+ }
+
+ return -1, nil, exif.ErrNoExif
+}
+
+// FindXmp returns the the segment that hosts the XMP data (if present).
+func (sl *SegmentList) FindXmp() (index int, segment *Segment, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ for i, s := range sl.segments {
+ if s.IsXmp() == true {
+ return i, s, nil
+ }
+ }
+
+ return -1, nil, ErrNoXmp
+}
+
+// FindIptc returns the the segment that hosts the IPTC data (if present).
+func (sl *SegmentList) FindIptc() (index int, segment *Segment, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ for i, s := range sl.segments {
+ if s.IsIptc() == true {
+ return i, s, nil
+ }
+ }
+
+ return -1, nil, ErrNoIptc
+}
+
+// Exif returns an `exif.Ifd` instance for the EXIF data we currently have.
+func (sl *SegmentList) Exif() (rootIfd *exif.Ifd, rawExif []byte, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ _, s, err := sl.FindExif()
+ log.PanicIf(err)
+
+ rootIfd, rawExif, err = s.Exif()
+ log.PanicIf(err)
+
+ return rootIfd, rawExif, nil
+}
+
+// Iptc returns embedded IPTC data if present.
+func (sl *SegmentList) Iptc() (tags map[iptc.StreamTagKey][]iptc.TagData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // TODO(dustin): Add comment and return data.
+
+ _, s, err := sl.FindIptc()
+ log.PanicIf(err)
+
+ tags, err = s.Iptc()
+ log.PanicIf(err)
+
+ return tags, nil
+}
+
+// ConstructExifBuilder returns an `exif.IfdBuilder` instance (needed for
+// modifying) preloaded with all existing tags.
+func (sl *SegmentList) ConstructExifBuilder() (rootIb *exif.IfdBuilder, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ rootIfd, _, err := sl.Exif()
+ log.PanicIf(err)
+
+ ib := exif.NewIfdBuilderFromExistingChain(rootIfd)
+
+ return ib, nil
+}
+
+// DumpExif returns an unstructured list of tags (useful when just reviewing).
+func (sl *SegmentList) DumpExif() (segmentIndex int, segment *Segment, exifTags []exif.ExifTag, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ segmentIndex, s, err := sl.FindExif()
+ if err != nil {
+ if err == exif.ErrNoExif {
+ return 0, nil, nil, err
+ }
+
+ log.Panic(err)
+ }
+
+ exifTags, err = s.FlatExif()
+ log.PanicIf(err)
+
+ return segmentIndex, s, exifTags, nil
+}
+
+func makeEmptyExifSegment() (s *Segment) {
+
+ // TODO(dustin): Add test
+
+ return &Segment{
+ MarkerId: MARKER_APP1,
+ }
+}
+
+// SetExif encodes and sets EXIF data into the given segment. If `index` is -1,
+// append a new segment.
+func (sl *SegmentList) SetExif(ib *exif.IfdBuilder) (err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ _, s, err := sl.FindExif()
+ if err != nil {
+ if log.Is(err, exif.ErrNoExif) == false {
+ log.Panic(err)
+ }
+
+ s = makeEmptyExifSegment()
+
+ prefix := sl.segments[:1]
+
+ // Install it near the beginning where we know it's safe. We can't
+ // insert it after the EOI segment, and there might be more than one
+ // depending on implementation and/or lax adherence to the standard.
+ tail := append([]*Segment{s}, sl.segments[1:]...)
+
+ sl.segments = append(prefix, tail...)
+ }
+
+ err = s.SetExif(ib)
+ log.PanicIf(err)
+
+ return nil
+}
+
+// DropExif will drop the EXIF data if present.
+func (sl *SegmentList) DropExif() (wasDropped bool, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // TODO(dustin): Add test
+
+ i, _, err := sl.FindExif()
+ if err == nil {
+ // Found.
+ sl.segments = append(sl.segments[:i], sl.segments[i+1:]...)
+
+ return true, nil
+ } else if log.Is(err, exif.ErrNoExif) == false {
+ log.Panic(err)
+ }
+
+ // Not found.
+ return false, nil
+}
+
+// Write writes the segment data to the given `io.Writer`.
+func (sl *SegmentList) Write(w io.Writer) (err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ offset := 0
+
+ for i, s := range sl.segments {
+ h := sha1.New()
+ h.Write(s.Data)
+
+ // The scan-data will have a marker-ID of (0) because it doesn't have a
+ // marker-ID or length.
+ if s.MarkerId != 0 {
+ _, err := w.Write([]byte{0xff})
+ log.PanicIf(err)
+
+ offset++
+
+ _, err = w.Write([]byte{s.MarkerId})
+ log.PanicIf(err)
+
+ offset++
+
+ sizeLen, found := markerLen[s.MarkerId]
+ if found == false || sizeLen == 2 {
+ sizeLen = 2
+ l := uint16(len(s.Data) + sizeLen)
+
+ err = binary.Write(w, binary.BigEndian, &l)
+ log.PanicIf(err)
+
+ offset += 2
+ } else if sizeLen == 4 {
+ l := uint32(len(s.Data) + sizeLen)
+
+ err = binary.Write(w, binary.BigEndian, &l)
+ log.PanicIf(err)
+
+ offset += 4
+ } else if sizeLen != 0 {
+ log.Panicf("not a supported marker-size: SEGMENT-INDEX=(%d) MARKER-ID=(0x%02x) MARKER-SIZE-LEN=(%d)", i, s.MarkerId, sizeLen)
+ }
+ }
+
+ _, err := w.Write(s.Data)
+ log.PanicIf(err)
+
+ offset += len(s.Data)
+ }
+
+ return nil
+}