summaryrefslogtreecommitdiff
path: root/vendor/github.com/dsoprea
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/dsoprea')
-rw-r--r--vendor/github.com/dsoprea/go-exif/.travis.yml24
-rw-r--r--vendor/github.com/dsoprea/go-exif/README.md206
-rw-r--r--vendor/github.com/dsoprea/go-exif/error.go10
-rw-r--r--vendor/github.com/dsoprea/go-exif/exif.go247
-rw-r--r--vendor/github.com/dsoprea/go-exif/gps.go56
-rw-r--r--vendor/github.com/dsoprea/go-exif/ifd.go407
-rw-r--r--vendor/github.com/dsoprea/go-exif/ifd_builder.go1265
-rw-r--r--vendor/github.com/dsoprea/go-exif/ifd_builder_encode.go530
-rw-r--r--vendor/github.com/dsoprea/go-exif/ifd_enumerate.go1356
-rw-r--r--vendor/github.com/dsoprea/go-exif/ifd_tag_entry.go233
-rw-r--r--vendor/github.com/dsoprea/go-exif/package.go4
-rw-r--r--vendor/github.com/dsoprea/go-exif/parser.go190
-rw-r--r--vendor/github.com/dsoprea/go-exif/tag_type.go397
-rw-r--r--vendor/github.com/dsoprea/go-exif/tags.go229
-rw-r--r--vendor/github.com/dsoprea/go-exif/tags_data.go951
-rw-r--r--vendor/github.com/dsoprea/go-exif/tags_undefined.go417
-rw-r--r--vendor/github.com/dsoprea/go-exif/type.go310
-rw-r--r--vendor/github.com/dsoprea/go-exif/type_encode.go262
-rw-r--r--vendor/github.com/dsoprea/go-exif/v2/LICENSE9
-rw-r--r--vendor/github.com/dsoprea/go-exif/v2/ifd.go34
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/.MODULE_ROOT (renamed from vendor/github.com/dsoprea/go-exif/v2/.MODULE_ROOT)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/LICENSE (renamed from vendor/github.com/dsoprea/go-exif/LICENSE)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/ifd.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/ifd.go)16
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/parser.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/parser.go)63
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/testing_common.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/testing_common.go)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/type.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/type.go)122
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/utility.go (renamed from vendor/github.com/dsoprea/go-exif/utility.go)128
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/value_context.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/value_context.go)76
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/value_encoder.go)62
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/data_layer.go50
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/error.go (renamed from vendor/github.com/dsoprea/go-exif/v2/error.go)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/exif.go (renamed from vendor/github.com/dsoprea/go-exif/v2/exif.go)97
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/gps.go (renamed from vendor/github.com/dsoprea/go-exif/v2/gps.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/ifd_builder.go (renamed from vendor/github.com/dsoprea/go-exif/v2/ifd_builder.go)26
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/ifd_builder_encode.go (renamed from vendor/github.com/dsoprea/go-exif/v2/ifd_builder_encode.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/ifd_enumerate.go (renamed from vendor/github.com/dsoprea/go-exif/v2/ifd_enumerate.go)449
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/ifd_tag_entry.go (renamed from vendor/github.com/dsoprea/go-exif/v2/ifd_tag_entry.go)31
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/package.go (renamed from vendor/github.com/dsoprea/go-exif/v2/package.go)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/tags.go (renamed from vendor/github.com/dsoprea/go-exif/v2/tags.go)76
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/tags_data.go (renamed from vendor/github.com/dsoprea/go-exif/v2/tags_data.go)39
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/testing_common.go (renamed from vendor/github.com/dsoprea/go-exif/v2/testing_common.go)34
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/README.md (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/README.md)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/accessor.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/accessor.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_8828_oecf.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_8828_oecf.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9000_exif_version.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9000_exif_version.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9101_components_configuration.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9101_components_configuration.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_927C_maker_note.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_927C_maker_note.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9286_user_comment.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9286_user_comment.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A000_flashpix_version.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A000_flashpix_version.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A20C_spatial_frequency_response.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A20C_spatial_frequency_response.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A300_file_source.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A300_file_source.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A301_scene_type.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A301_scene_type.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A302_cfa_pattern.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A302_cfa_pattern.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/exif_iop_0002_interop_version.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/exif_iop_0002_interop_version.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/gps_001B_gps_processing_method.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/gps_001B_gps_processing_method.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/gps_001C_gps_area_information.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/gps_001C_gps_area_information.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/registration.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/registration.go)0
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/undefined/type.go (renamed from vendor/github.com/dsoprea/go-exif/v2/undefined/type.go)2
-rw-r--r--vendor/github.com/dsoprea/go-exif/v3/utility.go (renamed from vendor/github.com/dsoprea/go-exif/v2/utility.go)170
-rw-r--r--vendor/github.com/dsoprea/go-exif/value_context.go367
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/.travis.yml21
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/.MODULE_ROOT (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/.MODULE_ROOT)0
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/LICENSE (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/LICENSE)0
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/README.md (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/README.md)4
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/markers.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/markers.go)0
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/media_parser.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/media_parser.go)13
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/segment.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/segment.go)11
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/segment_list.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go)29
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/splitter.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/splitter.go)0
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/testing_common.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/testing_common.go)0
-rw-r--r--vendor/github.com/dsoprea/go-jpeg-image-structure/v2/utility.go (renamed from vendor/github.com/dsoprea/go-jpeg-image-structure/utility.go)0
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/.travis.yml21
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/README.md8
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/chunk_decoder.go89
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/media_parser.go106
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/testing_common.go64
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/utility.go65
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/.MODULE_ROOT (renamed from vendor/github.com/dsoprea/go-png-image-structure/.MODULE_ROOT)0
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/LICENSE (renamed from vendor/github.com/dsoprea/go-png-image-structure/LICENSE)0
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/chunk_decoder.go87
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/media_parser.go118
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/png.go (renamed from vendor/github.com/dsoprea/go-png-image-structure/png.go)14
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/testing_common.go64
-rw-r--r--vendor/github.com/dsoprea/go-png-image-structure/v2/utility.go (renamed from vendor/github.com/dsoprea/go-exif/v2/common/utility.go)16
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/LICENSE (renamed from vendor/github.com/dsoprea/go-utility/LICENSE)0
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/README.md64
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/bounceback.go273
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseekcloser.go95
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseeker.go156
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/calculate_seek.go52
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/common.go15
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/copy_bytes_between_positions.go40
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/does_exist.go19
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/graceful_copy.go54
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/list_files.go143
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/progress_wrapper.go93
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/read_counter.go36
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/readseeker_to_readerat.go63
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/readwriteseekcloser.go29
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/seekable_buffer.go146
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/simplefileinfo.go69
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/utility.go17
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/filesystem/write_counter.go36
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/image/README.md (renamed from vendor/github.com/dsoprea/go-utility/image/README.md)0
-rw-r--r--vendor/github.com/dsoprea/go-utility/v2/image/media_parser_type.go (renamed from vendor/github.com/dsoprea/go-utility/image/media_parser_type.go)2
105 files changed, 2709 insertions, 8400 deletions
diff --git a/vendor/github.com/dsoprea/go-exif/.travis.yml b/vendor/github.com/dsoprea/go-exif/.travis.yml
deleted file mode 100644
index 162896e30..000000000
--- a/vendor/github.com/dsoprea/go-exif/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: go
-go:
- - master
- - stable
- - "1.14"
- - "1.13"
- - "1.12"
-env:
- - GO111MODULE=on
-install:
- - go get -t ./...
-script:
-# v1
- - go test -v .
- - go test -v ./exif-read-tool
-# v2
- - cd v2
- - go test -v ./...
- - cd ..
-# v3. Coverage reports comes from this.
- - cd v3
- - go test -v ./... -coverprofile=coverage.txt -covermode=atomic
-after_success:
- - curl -s https://codecov.io/bash | bash
diff --git a/vendor/github.com/dsoprea/go-exif/README.md b/vendor/github.com/dsoprea/go-exif/README.md
deleted file mode 100644
index 972d58493..000000000
--- a/vendor/github.com/dsoprea/go-exif/README.md
+++ /dev/null
@@ -1,206 +0,0 @@
-[![Build Status](https://travis-ci.org/dsoprea/go-exif.svg?branch=master)](https://travis-ci.org/dsoprea/go-exif)
-[![codecov](https://codecov.io/gh/dsoprea/go-exif/branch/master/graph/badge.svg)](https://codecov.io/gh/dsoprea/go-exif)
-[![Go Report Card](https://goreportcard.com/badge/github.com/dsoprea/go-exif/v3)](https://goreportcard.com/report/github.com/dsoprea/go-exif/v3)
-[![GoDoc](https://godoc.org/github.com/dsoprea/go-exif/v3?status.svg)](https://godoc.org/github.com/dsoprea/go-exif/v3)
-
-# Overview
-
-This package provides native Go functionality to parse an existing EXIF block, update an existing EXIF block, or add a new EXIF block.
-
-
-# Getting
-
-To get the project and dependencies:
-
-```
-$ go get -t github.com/dsoprea/go-exif/v3
-```
-
-
-# Scope
-
-This project is concerned only with parsing and encoding raw EXIF data. It does
-not understand specific file-formats. This package assumes you know how to
-extract the raw EXIF data from a file, such as a JPEG, and, if you want to
-update it, know how to write it back. File-specific formats are not the concern
-of *go-exif*, though we provide
-[exif.SearchAndExtractExif][search-and-extract-exif] and
-[exif.SearchFileAndExtractExif][search-file-and-extract-exif] as brute-force
-search mechanisms that will help you explore the EXIF information for newer
-formats that you might not yet have any way to parse.
-
-That said, the author also provides the following projects to support the
-efficient processing of the corresponding image formats:
-
-- [go-jpeg-image-structure](https://github.com/dsoprea/go-jpeg-image-structure)
-- [go-png-image-structure](https://github.com/dsoprea/go-png-image-structure)
-- [go-tiff-image-structure](https://github.com/dsoprea/go-tiff-image-structure)
-- [go-heic-exif-extractor](https://github.com/dsoprea/go-heic-exif-extractor)
-
-See the [SetExif example in go-jpeg-image-structure][jpeg-set-exif] for
-practical information on getting started with JPEG files.
-
-
-# Usage
-
-The package provides a set of [working examples][examples] and is covered by
-unit-tests. Please look to these for getting familiar with how to read and write
-EXIF.
-
-Create an instance of the `Exif` type and call `Scan()` with a byte-slice, where
-the first byte is the beginning of the raw EXIF data. You may pass a callback
-that will be invoked for every tag or `nil` if you do not want one. If no
-callback is given, you are effectively just validating the structure or parsing
-of the image.
-
-Obviously, it is most efficient to properly parse the media file and then
-provide the specific EXIF data to be parsed, but there is also a heuristic for
-finding the EXIF data within the media blob, directly. This means that, at least
-for testing or curiosity, **you do not have to parse or even understand the
-format of image or audio file in order to find and decode the EXIF information
-inside of it.** See the usage of the `SearchAndExtractExif` method in the
-example.
-
-The library often refers to an IFD with an "IFD path" (e.g. IFD/Exif,
-IFD/GPSInfo). A "fully-qualified" IFD-path is one that includes an index
-describing which specific sibling IFD is being referred to if not the first one
-(e.g. IFD1, the IFD where the thumbnail is expressed per the TIFF standard).
-
-There is an "IFD mapping" and a "tag index" that must be created and passed to
-the library from the top. These contain all of the knowledge of the IFD
-hierarchies and their tag-IDs (the IFD mapping) and the tags that they are
-allowed to host (the tag index). There are convenience functions to load them
-with the standard TIFF information, but you, alternatively, may choose
-something totally different (to support parsing any kind of EXIF data that does
-not follow or is not relevant to TIFF at all).
-
-
-# Standards and Customization
-
-This project is configuration driven. By default, it has no knowledge of tags
-and IDs until you load them prior to using (which is incorporated in the
-examples). You are just as easily able to add additional custom IFDs and custom
-tags for them. If desired, you could completely ignore the standard information
-and load *totally* non-standard IFDs and tags.
-
-This would be useful for divergent implementations that add non-standard
-information to images. It would also be useful if there is some need to just
-store a flat list of tags in an image for simplified, proprietary usage.
-
-
-# Reader Tool
-
-There is a runnable reading/dumping tool included:
-
-```
-$ go get github.com/dsoprea/go-exif/v3/command/exif-read-tool
-$ exif-read-tool --filepath "<media file-path>"
-```
-
-Example output:
-
-```
-IFD-PATH=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]
-IFD-PATH=[IFD] ID=(0x0110) NAME=[Model] COUNT=(22) TYPE=[ASCII] VALUE=[Canon EOS 5D Mark III]
-IFD-PATH=[IFD] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[1]
-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=(0x0132) NAME=[DateTime] COUNT=(20) TYPE=[ASCII] VALUE=[2017:12:02 08:18:50]
-...
-```
-
-You can also print the raw, parsed data as JSON:
-
-```
-$ exif-read-tool --filepath "<media file-path>" -json
-```
-
-Example output:
-
-```
-[
- {
- "ifd_path": "IFD",
- "fq_ifd_path": "IFD",
- "ifd_index": 0,
- "tag_id": 271,
- "tag_name": "Make",
- "tag_type_id": 2,
- "tag_type_name": "ASCII",
- "unit_count": 6,
- "value": "Canon",
- "value_string": "Canon"
- },
- {
- "ifd_path": "IFD",
-...
-```
-
-
-# Testing
-
-The traditional method:
-
-```
-$ go test github.com/dsoprea/go-exif/v3/...
-```
-
-
-# Release Notes
-
-## v3 Release
-
-This release primarily introduces an interchangeable data-layer, where any
-`io.ReadSeeker` can be used to read EXIF data rather than necessarily loading
-the EXIF blob into memory first.
-
-Several backwards-incompatible clean-ups were also included in this release. See
-[releases][releases] for more information.
-
-## v2 Release
-
-Features a heavily reflowed interface that makes usage much simpler. The
-undefined-type tag-processing (which affects most photographic images) has also
-been overhauled and streamlined. It is now complete and stable. Adoption is
-strongly encouraged.
-
-
-# *Contributing*
-
-EXIF has an excellently-documented structure but there are a lot of devices and
-manufacturers out there. There are only so many files that we can personally
-find to test against, and most of these are images that have been generated only
-in the past few years. JPEG, being the largest implementor of EXIF, has been
-around for even longer (but not much). Therefore, there is a lot of
-compatibility to test for.
-
-**If you are able to help by running the included reader-tool against all of the
-EXIF-compatible files you have, it would be deeply appreciated. This is mostly
-going to be JPEG files (but not all variations). If you are able to test a large
-number of files (thousands or millions) then please post an issue mentioning how
-many files you have processed. If you had failures, then please share them and
-try to support efforts to understand them.**
-
-If you are able to test 100K+ files, I will give you credit on the project. The
-further back in time your images reach, the higher in the list your name/company
-will go.
-
-
-# Contributors/Testing
-
-Thank you to the following users for solving non-trivial issues, supporting the
-project with solving edge-case problems in specific images, or otherwise
-providing their non-trivial time or image corpus to test go-exif:
-
-- [philip-firstorder](https://github.com/philip-firstorder) (200K images)
-- [matchstick](https://github.com/matchstick) (102K images)
-
-In addition to these, it has been tested on my own collection, north of 478K
-images.
-
-[search-and-extract-exif]: https://godoc.org/github.com/dsoprea/go-exif/v3#SearchAndExtractExif
-[search-file-and-extract-exif]: https://godoc.org/github.com/dsoprea/go-exif/v3#SearchFileAndExtractExif
-[jpeg-set-exif]: https://godoc.org/github.com/dsoprea/go-jpeg-image-structure#example-SegmentList-SetExif
-[examples]: https://godoc.org/github.com/dsoprea/go-exif/v3#pkg-examples
-[releases]: https://github.com/dsoprea/go-exif/releases
diff --git a/vendor/github.com/dsoprea/go-exif/error.go b/vendor/github.com/dsoprea/go-exif/error.go
deleted file mode 100644
index 0e6e138cb..000000000
--- a/vendor/github.com/dsoprea/go-exif/error.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package exif
-
-import (
- "errors"
-)
-
-var (
- ErrTagNotFound = errors.New("tag not found")
- ErrTagNotStandard = errors.New("tag not a standard tag")
-)
diff --git a/vendor/github.com/dsoprea/go-exif/exif.go b/vendor/github.com/dsoprea/go-exif/exif.go
deleted file mode 100644
index 8d1b848f0..000000000
--- a/vendor/github.com/dsoprea/go-exif/exif.go
+++ /dev/null
@@ -1,247 +0,0 @@
-package exif
-
-import (
- "bytes"
- "errors"
- "fmt"
- "os"
-
- "encoding/binary"
- "io/ioutil"
-
- "github.com/dsoprea/go-logging"
-)
-
-const (
- // ExifAddressableAreaStart is the absolute offset in the file that all
- // offsets are relative to.
- ExifAddressableAreaStart = uint32(0x0)
-
- // ExifDefaultFirstIfdOffset is essentially the number of bytes in addition
- // to `ExifAddressableAreaStart` that you have to move in order to escape
- // the rest of the header and get to the earliest point where we can put
- // stuff (which has to be the first IFD). This is the size of the header
- // sequence containing the two-character byte-order, two-character fixed-
- // bytes, and the four bytes describing the first-IFD offset.
- ExifDefaultFirstIfdOffset = uint32(2 + 2 + 4)
-)
-
-var (
- exifLogger = log.NewLogger("exif.exif")
-
- // EncodeDefaultByteOrder is the default byte-order for encoding operations.
- EncodeDefaultByteOrder = binary.BigEndian
-
- // Default byte order for tests.
- TestDefaultByteOrder = binary.BigEndian
-
- BigEndianBoBytes = [2]byte{'M', 'M'}
- LittleEndianBoBytes = [2]byte{'I', 'I'}
-
- ByteOrderLookup = map[[2]byte]binary.ByteOrder{
- BigEndianBoBytes: binary.BigEndian,
- LittleEndianBoBytes: binary.LittleEndian,
- }
-
- ByteOrderLookupR = map[binary.ByteOrder][2]byte{
- binary.BigEndian: BigEndianBoBytes,
- binary.LittleEndian: LittleEndianBoBytes,
- }
-
- ExifFixedBytesLookup = map[binary.ByteOrder][2]byte{
- binary.LittleEndian: {0x2a, 0x00},
- binary.BigEndian: {0x00, 0x2a},
- }
-)
-
-var (
- ErrNoExif = errors.New("no exif data")
- ErrExifHeaderError = errors.New("exif header error")
-)
-
-// SearchAndExtractExif returns a slice from the beginning of the EXIF data to
-// end of the file (it's not practical to try and calculate where the data
-// actually ends; it needs to be formally parsed).
-func SearchAndExtractExif(data []byte) (rawExif []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
- }
- }()
-
- // Search for the beginning of the EXIF information. The EXIF is near the
- // beginning of our/most JPEGs, so this has a very low cost.
-
- foundAt := -1
- for i := 0; i < len(data); i++ {
- if _, err := ParseExifHeader(data[i:]); err == nil {
- foundAt = i
- break
- } else if log.Is(err, ErrNoExif) == false {
- return nil, err
- }
- }
-
- if foundAt == -1 {
- return nil, ErrNoExif
- }
-
- return data[foundAt:], nil
-}
-
-// SearchFileAndExtractExif returns a slice from the beginning of the EXIF data
-// to the end of the file (it's not practical to try and calculate where the
-// data actually ends).
-func SearchFileAndExtractExif(filepath string) (rawExif []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
- }
- }()
-
- // Open the file.
-
- f, err := os.Open(filepath)
- log.PanicIf(err)
-
- defer f.Close()
-
- data, err := ioutil.ReadAll(f)
- log.PanicIf(err)
-
- rawExif, err = SearchAndExtractExif(data)
- log.PanicIf(err)
-
- return rawExif, nil
-}
-
-type ExifHeader struct {
- ByteOrder binary.ByteOrder
- FirstIfdOffset uint32
-}
-
-func (eh ExifHeader) String() string {
- return fmt.Sprintf("ExifHeader<BYTE-ORDER=[%v] FIRST-IFD-OFFSET=(0x%02x)>", eh.ByteOrder, eh.FirstIfdOffset)
-}
-
-// ParseExifHeader parses the bytes at the very top of the header.
-//
-// This will panic with ErrNoExif on any data errors so that we can double as
-// an EXIF-detection routine.
-func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // Good reference:
- //
- // CIPA DC-008-2016; JEITA CP-3451D
- // -> http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
-
- if len(data) < 2 {
- exifLogger.Warningf(nil, "Not enough data for EXIF header (1): (%d)", len(data))
- return eh, ErrNoExif
- }
-
- byteOrderBytes := [2]byte{data[0], data[1]}
-
- byteOrder, found := ByteOrderLookup[byteOrderBytes]
- if found == false {
- // exifLogger.Warningf(nil, "EXIF byte-order not recognized: [%v]", byteOrderBytes)
- return eh, ErrNoExif
- }
-
- if len(data) < 4 {
- exifLogger.Warningf(nil, "Not enough data for EXIF header (2): (%d)", len(data))
- return eh, ErrNoExif
- }
-
- fixedBytes := [2]byte{data[2], data[3]}
- expectedFixedBytes := ExifFixedBytesLookup[byteOrder]
- if fixedBytes != expectedFixedBytes {
- // exifLogger.Warningf(nil, "EXIF header fixed-bytes should be [%v] but are: [%v]", expectedFixedBytes, fixedBytes)
- return eh, ErrNoExif
- }
-
- if len(data) < 2 {
- exifLogger.Warningf(nil, "Not enough data for EXIF header (3): (%d)", len(data))
- return eh, ErrNoExif
- }
-
- firstIfdOffset := byteOrder.Uint32(data[4:8])
-
- eh = ExifHeader{
- ByteOrder: byteOrder,
- FirstIfdOffset: firstIfdOffset,
- }
-
- return eh, nil
-}
-
-// Visit recursively invokes a callback for every tag.
-func Visit(rootIfdName string, ifdMapping *IfdMapping, tagIndex *TagIndex, exifData []byte, visitor RawTagVisitor) (eh ExifHeader, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- eh, err = ParseExifHeader(exifData)
- log.PanicIf(err)
-
- ie := NewIfdEnumerate(ifdMapping, tagIndex, exifData, eh.ByteOrder)
-
- err = ie.Scan(rootIfdName, eh.FirstIfdOffset, visitor, true)
- log.PanicIf(err)
-
- return eh, nil
-}
-
-// Collect recursively builds a static structure of all IFDs and tags.
-func Collect(ifdMapping *IfdMapping, tagIndex *TagIndex, exifData []byte) (eh ExifHeader, index IfdIndex, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- eh, err = ParseExifHeader(exifData)
- log.PanicIf(err)
-
- ie := NewIfdEnumerate(ifdMapping, tagIndex, exifData, eh.ByteOrder)
-
- index, err = ie.Collect(eh.FirstIfdOffset, true)
- log.PanicIf(err)
-
- return eh, index, nil
-}
-
-// BuildExifHeader constructs the bytes that go in the very beginning.
-func BuildExifHeader(byteOrder binary.ByteOrder, firstIfdOffset uint32) (headerBytes []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- b := new(bytes.Buffer)
-
- // This is the point in the data that all offsets are relative to.
- boBytes := ByteOrderLookupR[byteOrder]
- _, err = b.WriteString(string(boBytes[:]))
- log.PanicIf(err)
-
- fixedBytes := ExifFixedBytesLookup[byteOrder]
-
- _, err = b.Write(fixedBytes[:])
- log.PanicIf(err)
-
- err = binary.Write(b, byteOrder, firstIfdOffset)
- log.PanicIf(err)
-
- return b.Bytes(), nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/gps.go b/vendor/github.com/dsoprea/go-exif/gps.go
deleted file mode 100644
index 7d74f22d3..000000000
--- a/vendor/github.com/dsoprea/go-exif/gps.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package exif
-
-import (
- "errors"
- "fmt"
- "time"
-
- "github.com/golang/geo/s2"
-)
-
-var (
- ErrGpsCoordinatesNotValid = errors.New("GPS coordinates not valid")
-)
-
-type GpsDegrees struct {
- Orientation byte
- Degrees, Minutes, Seconds float64
-}
-
-func (d GpsDegrees) String() string {
- return fmt.Sprintf("Degrees<O=[%s] D=(%g) M=(%g) S=(%g)>", string([]byte{d.Orientation}), d.Degrees, d.Minutes, d.Seconds)
-}
-
-func (d GpsDegrees) Decimal() float64 {
- decimal := float64(d.Degrees) + float64(d.Minutes)/60.0 + float64(d.Seconds)/3600.0
-
- if d.Orientation == 'S' || d.Orientation == 'W' {
- return -decimal
- } else {
- return decimal
- }
-}
-
-type GpsInfo struct {
- Latitude, Longitude GpsDegrees
- Altitude int
- Timestamp time.Time
-}
-
-func (gi *GpsInfo) String() string {
- return fmt.Sprintf("GpsInfo<LAT=(%.05f) LON=(%.05f) ALT=(%d) TIME=[%s]>", gi.Latitude.Decimal(), gi.Longitude.Decimal(), gi.Altitude, gi.Timestamp)
-}
-
-func (gi *GpsInfo) S2CellId() s2.CellID {
- latitude := gi.Latitude.Decimal()
- longitude := gi.Longitude.Decimal()
-
- ll := s2.LatLngFromDegrees(latitude, longitude)
- cellId := s2.CellIDFromLatLng(ll)
-
- if cellId.IsValid() == false {
- panic(ErrGpsCoordinatesNotValid)
- }
-
- return cellId
-}
diff --git a/vendor/github.com/dsoprea/go-exif/ifd.go b/vendor/github.com/dsoprea/go-exif/ifd.go
deleted file mode 100644
index e75404ddc..000000000
--- a/vendor/github.com/dsoprea/go-exif/ifd.go
+++ /dev/null
@@ -1,407 +0,0 @@
-package exif
-
-import (
- "errors"
- "fmt"
- "strings"
-
- "github.com/dsoprea/go-logging"
-)
-
-const (
- // IFD names. The paths that we referred to the IFDs with are comprised of
- // these.
-
- IfdStandard = "IFD"
- IfdExif = "Exif"
- IfdGps = "GPSInfo"
- IfdIop = "Iop"
-
- // Tag IDs for child IFDs.
-
- IfdExifId = 0x8769
- IfdGpsId = 0x8825
- IfdIopId = 0xA005
-
- // Just a placeholder.
-
- IfdRootId = 0x0000
-
- // The paths of the standard IFDs expressed in the standard IFD-mappings
- // and as the group-names in the tag data.
-
- IfdPathStandard = "IFD"
- IfdPathStandardExif = "IFD/Exif"
- IfdPathStandardExifIop = "IFD/Exif/Iop"
- IfdPathStandardGps = "IFD/GPSInfo"
-)
-
-var (
- ifdLogger = log.NewLogger("exif.ifd")
-)
-
-var (
- ErrChildIfdNotMapped = errors.New("no child-IFD for that tag-ID under parent")
-)
-
-// type IfdIdentity struct {
-// ParentIfdName string
-// IfdName string
-// }
-
-// func (ii IfdIdentity) String() string {
-// return fmt.Sprintf("IfdIdentity<PARENT-NAME=[%s] NAME=[%s]>", ii.ParentIfdName, ii.IfdName)
-// }
-
-type MappedIfd struct {
- ParentTagId uint16
- Placement []uint16
- Path []string
-
- Name string
- TagId uint16
- Children map[uint16]*MappedIfd
-}
-
-func (mi *MappedIfd) String() string {
- pathPhrase := mi.PathPhrase()
- return fmt.Sprintf("MappedIfd<(0x%04X) [%s] PATH=[%s]>", mi.TagId, mi.Name, pathPhrase)
-}
-
-func (mi *MappedIfd) PathPhrase() string {
- return strings.Join(mi.Path, "/")
-}
-
-// IfdMapping describes all of the IFDs that we currently recognize.
-type IfdMapping struct {
- rootNode *MappedIfd
-}
-
-func NewIfdMapping() (ifdMapping *IfdMapping) {
- rootNode := &MappedIfd{
- Path: make([]string, 0),
- Children: make(map[uint16]*MappedIfd),
- }
-
- return &IfdMapping{
- rootNode: rootNode,
- }
-}
-
-func NewIfdMappingWithStandard() (ifdMapping *IfdMapping) {
- defer func() {
- if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
- }
- }()
-
- im := NewIfdMapping()
-
- err := LoadStandardIfds(im)
- log.PanicIf(err)
-
- return im
-}
-
-func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ptr := im.rootNode
- for _, tagId := range parentPlacement {
- if descendantPtr, found := ptr.Children[tagId]; found == false {
- log.Panicf("ifd child with tag-ID (%04x) not registered: [%s]", tagId, ptr.PathPhrase())
- } else {
- ptr = descendantPtr
- }
- }
-
- return ptr, nil
-}
-
-func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if pathPhrase == "" {
- log.Panicf("path-phrase is empty")
- }
-
- path := strings.Split(pathPhrase, "/")
- ptr := im.rootNode
-
- for _, name := range path {
- var hit *MappedIfd
- for _, mi := range ptr.Children {
- if mi.Name == name {
- hit = mi
- break
- }
- }
-
- if hit == nil {
- log.Panicf("ifd child with name [%s] not registered: [%s]", name, ptr.PathPhrase())
- }
-
- ptr = hit
- }
-
- return ptr, nil
-}
-
-// GetChild is a convenience function to get the child path for a given parent
-// placement and child tag-ID.
-func (im *IfdMapping) GetChild(parentPathPhrase string, tagId uint16) (mi *MappedIfd, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- mi, err = im.GetWithPath(parentPathPhrase)
- log.PanicIf(err)
-
- for _, childMi := range mi.Children {
- if childMi.TagId == tagId {
- return childMi, nil
- }
- }
-
- // Whether or not an IFD is defined in data, such an IFD is not registered
- // and would be unknown.
- log.Panic(ErrChildIfdNotMapped)
- return nil, nil
-}
-
-type IfdTagIdAndIndex struct {
- Name string
- TagId uint16
- Index int
-}
-
-func (itii IfdTagIdAndIndex) String() string {
- return fmt.Sprintf("IfdTagIdAndIndex<NAME=[%s] ID=(%04x) INDEX=(%d)>", itii.Name, itii.TagId, itii.Index)
-}
-
-// ResolvePath takes a list of names, which can also be suffixed with indices
-// (to identify the second, third, etc.. sibling IFD) and returns a list of
-// tag-IDs and those indices.
-//
-// Example:
-//
-// - IFD/Exif/Iop
-// - IFD0/Exif/Iop
-//
-// This is the only call that supports adding the numeric indices.
-func (im *IfdMapping) ResolvePath(pathPhrase string) (lineage []IfdTagIdAndIndex, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- pathPhrase = strings.TrimSpace(pathPhrase)
-
- if pathPhrase == "" {
- log.Panicf("can not resolve empty path-phrase")
- }
-
- path := strings.Split(pathPhrase, "/")
- lineage = make([]IfdTagIdAndIndex, len(path))
-
- ptr := im.rootNode
- empty := IfdTagIdAndIndex{}
- for i, name := range path {
- indexByte := name[len(name)-1]
- index := 0
- if indexByte >= '0' && indexByte <= '9' {
- index = int(indexByte - '0')
- name = name[:len(name)-1]
- }
-
- itii := IfdTagIdAndIndex{}
- for _, mi := range ptr.Children {
- if mi.Name != name {
- continue
- }
-
- itii.Name = name
- itii.TagId = mi.TagId
- itii.Index = index
-
- ptr = mi
-
- break
- }
-
- if itii == empty {
- log.Panicf("ifd child with name [%s] not registered: [%s]", name, pathPhrase)
- }
-
- lineage[i] = itii
- }
-
- return lineage, nil
-}
-
-func (im *IfdMapping) FqPathPhraseFromLineage(lineage []IfdTagIdAndIndex) (fqPathPhrase string) {
- fqPathParts := make([]string, len(lineage))
- for i, itii := range lineage {
- if itii.Index > 0 {
- fqPathParts[i] = fmt.Sprintf("%s%d", itii.Name, itii.Index)
- } else {
- fqPathParts[i] = itii.Name
- }
- }
-
- return strings.Join(fqPathParts, "/")
-}
-
-func (im *IfdMapping) PathPhraseFromLineage(lineage []IfdTagIdAndIndex) (pathPhrase string) {
- pathParts := make([]string, len(lineage))
- for i, itii := range lineage {
- pathParts[i] = itii.Name
- }
-
- return strings.Join(pathParts, "/")
-}
-
-// StripPathPhraseIndices returns a non-fully-qualified path-phrase (no
-// indices).
-func (im *IfdMapping) StripPathPhraseIndices(pathPhrase string) (strippedPathPhrase string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- lineage, err := im.ResolvePath(pathPhrase)
- log.PanicIf(err)
-
- strippedPathPhrase = im.PathPhraseFromLineage(lineage)
- return strippedPathPhrase, nil
-}
-
-// Add puts the given IFD at the given position of the tree. The position of the
-// tree is referred to as the placement and is represented by a set of tag-IDs,
-// where the leftmost is the root tag and the tags going to the right are
-// progressive descendants.
-func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! It would be nicer to provide a list of names in the placement rather than tag-IDs.
-
- ptr, err := im.Get(parentPlacement)
- log.PanicIf(err)
-
- path := make([]string, len(parentPlacement)+1)
- if len(parentPlacement) > 0 {
- copy(path, ptr.Path)
- }
-
- path[len(path)-1] = name
-
- placement := make([]uint16, len(parentPlacement)+1)
- if len(placement) > 0 {
- copy(placement, ptr.Placement)
- }
-
- placement[len(placement)-1] = tagId
-
- childIfd := &MappedIfd{
- ParentTagId: ptr.TagId,
- Path: path,
- Placement: placement,
- Name: name,
- TagId: tagId,
- Children: make(map[uint16]*MappedIfd),
- }
-
- if _, found := ptr.Children[tagId]; found == true {
- log.Panicf("child IFD with tag-ID (%04x) already registered under IFD [%s] with tag-ID (%04x)", tagId, ptr.Name, ptr.TagId)
- }
-
- ptr.Children[tagId] = childIfd
-
- return nil
-}
-
-func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input []string) (output []string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- currentIfd := stack[len(stack)-1]
-
- output = input
- for _, childIfd := range currentIfd.Children {
- stackCopy := make([]*MappedIfd, len(stack)+1)
-
- copy(stackCopy, stack)
- stackCopy[len(stack)] = childIfd
-
- // Add to output, but don't include the obligatory root node.
- parts := make([]string, len(stackCopy)-1)
- for i, mi := range stackCopy[1:] {
- parts[i] = mi.Name
- }
-
- output = append(output, strings.Join(parts, "/"))
-
- output, err = im.dumpLineages(stackCopy, output)
- log.PanicIf(err)
- }
-
- return output, nil
-}
-
-func (im *IfdMapping) DumpLineages() (output []string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- stack := []*MappedIfd{im.rootNode}
- output = make([]string, 0)
-
- output, err = im.dumpLineages(stack, output)
- log.PanicIf(err)
-
- return output, nil
-}
-
-func LoadStandardIfds(im *IfdMapping) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = im.Add([]uint16{}, IfdRootId, IfdStandard)
- log.PanicIf(err)
-
- err = im.Add([]uint16{IfdRootId}, IfdExifId, IfdExif)
- log.PanicIf(err)
-
- err = im.Add([]uint16{IfdRootId, IfdExifId}, IfdIopId, IfdIop)
- log.PanicIf(err)
-
- err = im.Add([]uint16{IfdRootId}, IfdGpsId, IfdGps)
- log.PanicIf(err)
-
- return nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/ifd_builder.go b/vendor/github.com/dsoprea/go-exif/ifd_builder.go
deleted file mode 100644
index 40ef4dc4f..000000000
--- a/vendor/github.com/dsoprea/go-exif/ifd_builder.go
+++ /dev/null
@@ -1,1265 +0,0 @@
-package exif
-
-// NOTES:
-//
-// The thumbnail offset and length tags shouldn't be set directly. Use the
-// (*IfdBuilder).SetThumbnail() method instead.
-
-import (
- "errors"
- "fmt"
- "strings"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-var (
- ifdBuilderLogger = log.NewLogger("exif.ifd_builder")
-)
-
-var (
- ErrTagEntryNotFound = errors.New("tag entry not found")
- ErrChildIbNotFound = errors.New("child IB not found")
-)
-
-type IfdBuilderTagValue struct {
- valueBytes []byte
- ib *IfdBuilder
-}
-
-func (ibtv IfdBuilderTagValue) String() string {
- if ibtv.IsBytes() == true {
- var valuePhrase string
- if len(ibtv.valueBytes) <= 8 {
- valuePhrase = fmt.Sprintf("%v", ibtv.valueBytes)
- } else {
- valuePhrase = fmt.Sprintf("%v...", ibtv.valueBytes[:8])
- }
-
- return fmt.Sprintf("IfdBuilderTagValue<BYTES=%v LEN=(%d)>", valuePhrase, len(ibtv.valueBytes))
- } else if ibtv.IsIb() == true {
- return fmt.Sprintf("IfdBuilderTagValue<IB=%s>", ibtv.ib)
- } else {
- log.Panicf("IBTV state undefined")
- return ""
- }
-}
-
-func NewIfdBuilderTagValueFromBytes(valueBytes []byte) *IfdBuilderTagValue {
- return &IfdBuilderTagValue{
- valueBytes: valueBytes,
- }
-}
-
-func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue {
- return &IfdBuilderTagValue{
- ib: ib,
- }
-}
-
-// IsBytes returns true if the bytes are populated. This is always the case
-// when we're loaded from a tag in an existing IFD.
-func (ibtv IfdBuilderTagValue) IsBytes() bool {
- return ibtv.valueBytes != nil
-}
-
-func (ibtv IfdBuilderTagValue) Bytes() []byte {
- if ibtv.IsBytes() == false {
- log.Panicf("this tag is not a byte-slice value")
- } else if ibtv.IsIb() == true {
- log.Panicf("this tag is an IFD-builder value not a byte-slice")
- }
-
- return ibtv.valueBytes
-}
-
-func (ibtv IfdBuilderTagValue) IsIb() bool {
- return ibtv.ib != nil
-}
-
-func (ibtv IfdBuilderTagValue) Ib() *IfdBuilder {
- if ibtv.IsIb() == false {
- log.Panicf("this tag is not an IFD-builder value")
- } else if ibtv.IsBytes() == true {
- log.Panicf("this tag is a byte-slice, not a IFD-builder")
- }
-
- return ibtv.ib
-}
-
-type BuilderTag struct {
- // ifdPath is the path of the IFD that hosts this tag.
- ifdPath string
-
- tagId uint16
- typeId TagTypePrimitive
-
- // 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.
- value *IfdBuilderTagValue
-
- // byteOrder is the byte order. It's chiefly/originally here to support
- // printing the value.
- byteOrder binary.ByteOrder
-}
-
-func NewBuilderTag(ifdPath string, tagId uint16, typeId TagTypePrimitive, value *IfdBuilderTagValue, byteOrder binary.ByteOrder) *BuilderTag {
- return &BuilderTag{
- ifdPath: ifdPath,
- tagId: tagId,
- typeId: typeId,
- value: value,
- byteOrder: byteOrder,
- }
-}
-
-func NewChildIfdBuilderTag(ifdPath string, tagId uint16, value *IfdBuilderTagValue) *BuilderTag {
- return &BuilderTag{
- ifdPath: ifdPath,
- tagId: tagId,
- typeId: TypeLong,
- value: value,
- }
-}
-
-func (bt *BuilderTag) Value() (value *IfdBuilderTagValue) {
- return bt.value
-}
-
-func (bt *BuilderTag) String() string {
- var valueString string
-
- if bt.value.IsBytes() == true {
- var err error
-
- valueString, err = Format(bt.value.Bytes(), bt.typeId, false, bt.byteOrder)
- log.PanicIf(err)
- } else {
- valueString = fmt.Sprintf("%v", bt.value)
- }
-
- return fmt.Sprintf("BuilderTag<IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] VALUE=[%s]>", bt.ifdPath, bt.tagId, TypeNames[bt.typeId], valueString)
-}
-
-func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Add test.
-
- tt := NewTagType(bt.typeId, byteOrder)
- ve := NewValueEncoder(byteOrder)
-
- var ed EncodedData
- if bt.typeId == TypeUndefined {
- var err error
-
- ed, err = EncodeUndefined(bt.ifdPath, bt.tagId, value)
- log.PanicIf(err)
- } else {
- var err error
-
- ed, err = ve.EncodeWithType(tt, value)
- log.PanicIf(err)
- }
-
- bt.value = NewIfdBuilderTagValueFromBytes(ed.Encoded)
-
- return nil
-}
-
-// NewStandardBuilderTag constructs a `BuilderTag` instance. The type is looked
-// up. `ii` is the type of IFD that owns this tag.
-func NewStandardBuilderTag(ifdPath string, it *IndexedTag, byteOrder binary.ByteOrder, value interface{}) *BuilderTag {
- typeId := it.Type
- tt := NewTagType(typeId, byteOrder)
-
- ve := NewValueEncoder(byteOrder)
-
- var ed EncodedData
- if it.Type == TypeUndefined {
- var err error
-
- ed, err = EncodeUndefined(ifdPath, it.Id, value)
- log.PanicIf(err)
- } else {
- var err error
-
- ed, err = ve.EncodeWithType(tt, value)
- log.PanicIf(err)
- }
-
- tagValue := NewIfdBuilderTagValueFromBytes(ed.Encoded)
-
- return NewBuilderTag(
- ifdPath,
- it.Id,
- typeId,
- tagValue,
- byteOrder)
-}
-
-type IfdBuilder struct {
- // ifdName is the name of the IFD represented by this instance.
- name string
-
- // ifdPath is the path of the IFD represented by this instance.
- ifdPath string
-
- // fqIfdPath is the fully-qualified path of the IFD represented by this
- // instance.
- fqIfdPath string
-
- // ifdTagId will be non-zero if we're a child IFD.
- ifdTagId uint16
-
- byteOrder binary.ByteOrder
-
- // Includes both normal tags and IFD tags (which point to child IFDs).
- // TODO(dustin): Keep a separate list of children like with `Ifd`.
- // TODO(dustin): Either rename this or `Entries` in `Ifd` to be the same thing.
- tags []*BuilderTag
-
- // existingOffset will be the offset that this IFD is currently found at if
- // it represents an IFD that has previously been stored (or 0 if not).
- existingOffset uint32
-
- // nextIb represents the next link if we're chaining to another.
- nextIb *IfdBuilder
-
- // thumbnailData is populated with thumbnail data if there was thumbnail
- // data. Otherwise, it's nil.
- thumbnailData []byte
-
- ifdMapping *IfdMapping
- tagIndex *TagIndex
-}
-
-func NewIfdBuilder(ifdMapping *IfdMapping, tagIndex *TagIndex, fqIfdPath string, byteOrder binary.ByteOrder) (ib *IfdBuilder) {
- ifdPath, err := ifdMapping.StripPathPhraseIndices(fqIfdPath)
- log.PanicIf(err)
-
- var ifdTagId uint16
-
- mi, err := ifdMapping.GetWithPath(ifdPath)
- if err == nil {
- ifdTagId = mi.TagId
- } else if log.Is(err, ErrChildIfdNotMapped) == false {
- log.Panic(err)
- }
-
- ib = &IfdBuilder{
- // The right-most part of the IFD-path.
- name: mi.Name,
-
- // ifdPath describes the current IFD placement within the IFD tree.
- ifdPath: ifdPath,
-
- // fqIfdPath describes the current IFD placement within the IFD tree as
- // well as being qualified with non-zero indices.
- fqIfdPath: fqIfdPath,
-
- // ifdTagId is empty unless it's a child-IFD.
- ifdTagId: ifdTagId,
-
- byteOrder: byteOrder,
- tags: make([]*BuilderTag, 0),
-
- ifdMapping: ifdMapping,
- tagIndex: tagIndex,
- }
-
- return ib
-}
-
-// NewIfdBuilderWithExistingIfd creates a new IB using the same header type
-// information as the given IFD.
-func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
- name := ifd.Name
- ifdPath := ifd.IfdPath
- fqIfdPath := ifd.FqIfdPath
-
- var ifdTagId uint16
-
- // There is no tag-ID for the root IFD. It will never be a child IFD.
- if ifdPath != IfdPathStandard {
- mi, err := ifd.ifdMapping.GetWithPath(ifdPath)
- log.PanicIf(err)
-
- ifdTagId = mi.TagId
- }
-
- ib = &IfdBuilder{
- name: name,
- ifdPath: ifdPath,
- fqIfdPath: fqIfdPath,
- ifdTagId: ifdTagId,
- byteOrder: ifd.ByteOrder,
- existingOffset: ifd.Offset,
- ifdMapping: ifd.ifdMapping,
- tagIndex: ifd.tagIndex,
- }
-
- return ib
-}
-
-// NewIfdBuilderFromExistingChain creates a chain of IB instances from an
-// IFD chain generated from real data.
-func NewIfdBuilderFromExistingChain(rootIfd *Ifd, itevr *IfdTagEntryValueResolver) (firstIb *IfdBuilder) {
- // OBSOLETE(dustin): Support for `itevr` is now obsolete. This parameter will be removed in the future.
-
- var lastIb *IfdBuilder
- i := 0
- for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd {
- newIb := NewIfdBuilder(rootIfd.ifdMapping, rootIfd.tagIndex, rootIfd.FqIfdPath, thisExistingIfd.ByteOrder)
- if firstIb == nil {
- firstIb = newIb
- } else {
- lastIb.SetNextIb(newIb)
- }
-
- err := newIb.AddTagsFromExisting(thisExistingIfd, nil, nil, nil)
- log.PanicIf(err)
-
- lastIb = newIb
- i++
- }
-
- return firstIb
-}
-
-func (ib *IfdBuilder) NextIb() (nextIb *IfdBuilder, err error) {
- return ib.nextIb, nil
-}
-
-func (ib *IfdBuilder) ChildWithTagId(childIfdTagId uint16) (childIb *IfdBuilder, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- for _, bt := range ib.tags {
- if bt.value.IsIb() == false {
- continue
- }
-
- childIbThis := bt.value.Ib()
-
- if childIbThis.ifdTagId == childIfdTagId {
- return childIbThis, nil
- }
- }
-
- log.Panic(ErrChildIbNotFound)
-
- // Never reached.
- return nil, nil
-}
-
-func getOrCreateIbFromRootIbInner(rootIb *IfdBuilder, parentIb *IfdBuilder, currentLineage []IfdTagIdAndIndex) (ib *IfdBuilder, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Add test.
-
- thisIb := rootIb
-
- // Since we're calling ourselves recursively with incrementally different
- // paths, the FQ IFD-path of the parent that called us needs to be passed
- // in, in order for us to know it.
- var parentLineage []IfdTagIdAndIndex
- if parentIb != nil {
- var err error
-
- parentLineage, err = thisIb.ifdMapping.ResolvePath(parentIb.fqIfdPath)
- log.PanicIf(err)
- }
-
- // Process the current path part.
- currentItii := currentLineage[0]
-
- // Make sure the leftmost part of the FQ IFD-path agrees with the IB we
- // were given.
-
- expectedFqRootIfdPath := ""
- if parentLineage != nil {
- expectedLineage := append(parentLineage, currentItii)
- expectedFqRootIfdPath = thisIb.ifdMapping.PathPhraseFromLineage(expectedLineage)
- } else {
- expectedFqRootIfdPath = thisIb.ifdMapping.PathPhraseFromLineage(currentLineage[:1])
- }
-
- if expectedFqRootIfdPath != thisIb.fqIfdPath {
- log.Panicf("the FQ IFD-path [%s] we were given does not match the builder's FQ IFD-path [%s]", expectedFqRootIfdPath, thisIb.fqIfdPath)
- }
-
- // If we actually wanted a sibling (currentItii.Index > 0) then seek to it,
- // appending new siblings, as required, until we get there.
- for i := 0; i < currentItii.Index; i++ {
- if thisIb.nextIb == nil {
- // Generate an FQ IFD-path for the sibling. It'll use the same
- // non-FQ IFD-path as the current IB.
-
- siblingFqIfdPath := ""
- if parentLineage != nil {
- siblingFqIfdPath = fmt.Sprintf("%s/%s%d", parentIb.fqIfdPath, currentItii.Name, i+1)
- } else {
- siblingFqIfdPath = fmt.Sprintf("%s%d", currentItii.Name, i+1)
- }
-
- thisIb.nextIb = NewIfdBuilder(thisIb.ifdMapping, thisIb.tagIndex, siblingFqIfdPath, thisIb.byteOrder)
- }
-
- thisIb = thisIb.nextIb
- }
-
- // There is no child IFD to process. We're done.
- if len(currentLineage) == 1 {
- return thisIb, nil
- }
-
- // Establish the next child to be processed.
-
- childItii := currentLineage[1]
-
- var foundChild *IfdBuilder
- for _, bt := range thisIb.tags {
- if bt.value.IsIb() == false {
- continue
- }
-
- childIb := bt.value.Ib()
-
- if childIb.ifdTagId == childItii.TagId {
- foundChild = childIb
- break
- }
- }
-
- // If we didn't find the child, add it.
- if foundChild == nil {
- thisIbLineage, err := thisIb.ifdMapping.ResolvePath(thisIb.fqIfdPath)
- log.PanicIf(err)
-
- childLineage := make([]IfdTagIdAndIndex, len(thisIbLineage)+1)
- copy(childLineage, thisIbLineage)
-
- childLineage[len(childLineage)-1] = childItii
-
- fqIfdChildPath := thisIb.ifdMapping.FqPathPhraseFromLineage(childLineage)
-
- foundChild = NewIfdBuilder(thisIb.ifdMapping, thisIb.tagIndex, fqIfdChildPath, thisIb.byteOrder)
-
- err = thisIb.AddChildIb(foundChild)
- log.PanicIf(err)
- }
-
- finalIb, err := getOrCreateIbFromRootIbInner(foundChild, thisIb, currentLineage[1:])
- log.PanicIf(err)
-
- return finalIb, nil
-}
-
-// GetOrCreateIbFromRootIb returns an IB representing the requested IFD, even if
-// an IB doesn't already exist for it. This function may call itself
-// recursively.
-func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, fqIfdPath string) (ib *IfdBuilder, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // lineage is a necessity of our recursion process. It doesn't include any
- // parent IFDs on its left-side; it starts with the current IB only.
- lineage, err := rootIb.ifdMapping.ResolvePath(fqIfdPath)
- log.PanicIf(err)
-
- ib, err = getOrCreateIbFromRootIbInner(rootIb, nil, lineage)
- log.PanicIf(err)
-
- return ib, nil
-}
-
-func (ib *IfdBuilder) String() string {
- nextIfdPhrase := ""
- if ib.nextIb != nil {
- // TODO(dustin): We were setting this to ii.String(), but we were getting hex-data when printing this after building from an existing chain.
- nextIfdPhrase = ib.nextIb.ifdPath
- }
-
- return fmt.Sprintf("IfdBuilder<PATH=[%s] TAG-ID=(0x%04x) COUNT=(%d) OFF=(0x%04x) NEXT-IFD-PATH=[%s]>", ib.ifdPath, ib.ifdTagId, len(ib.tags), ib.existingOffset, nextIfdPhrase)
-}
-
-func (ib *IfdBuilder) Tags() (tags []*BuilderTag) {
- return ib.tags
-}
-
-// SetThumbnail sets thumbnail data.
-//
-// NOTES:
-//
-// - We don't manage any facet of the thumbnail data. This is the
-// responsibility of the user/developer.
-// - This method will fail unless the thumbnail is set on a the root IFD.
-// However, in order to be valid, it must be set on the second one, linked to
-// by the first, as per the EXIF/TIFF specification.
-// - We set the offset to (0) now but will allocate the data and properly assign
-// the offset when the IB is encoded (later).
-func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if ib.ifdPath != IfdPathStandard {
- log.Panicf("thumbnails can only go into a root Ifd (and only the second one)")
- }
-
- // TODO(dustin): !! Add a test for this function.
-
- if data == nil || len(data) == 0 {
- log.Panic("thumbnail is empty")
- }
-
- ib.thumbnailData = data
-
- ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData)
- offsetBt :=
- NewBuilderTag(
- ib.ifdPath,
- ThumbnailOffsetTagId,
- TypeLong,
- ibtvfb,
- ib.byteOrder)
-
- err = ib.Set(offsetBt)
- log.PanicIf(err)
-
- thumbnailSizeIt, err := ib.tagIndex.Get(ib.ifdPath, ThumbnailSizeTagId)
- log.PanicIf(err)
-
- sizeBt := NewStandardBuilderTag(ib.ifdPath, thumbnailSizeIt, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))})
-
- err = ib.Set(sizeBt)
- log.PanicIf(err)
-
- return nil
-}
-
-func (ib *IfdBuilder) Thumbnail() []byte {
- return ib.thumbnailData
-}
-
-func (ib *IfdBuilder) printTagTree(levels int) {
- indent := strings.Repeat(" ", levels*2)
-
- i := 0
- for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb {
- prefix := " "
- if i > 0 {
- prefix = ">"
- }
-
- if levels == 0 {
- fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, currentIb, i)
- } else {
- fmt.Printf("%s%sChild IFD: %s\n", indent, prefix, currentIb)
- }
-
- if len(currentIb.tags) > 0 {
- fmt.Printf("\n")
-
- for i, tag := range currentIb.tags {
- isChildIb := false
- _, err := ib.ifdMapping.GetChild(currentIb.ifdPath, tag.tagId)
- if err == nil {
- isChildIb = true
- } else if log.Is(err, ErrChildIfdNotMapped) == false {
- log.Panic(err)
- }
-
- tagName := ""
-
- // If a normal tag (not a child IFD) get the name.
- if isChildIb == true {
- tagName = "<Child IFD>"
- } else {
- it, err := ib.tagIndex.Get(tag.ifdPath, tag.tagId)
- if log.Is(err, ErrTagNotFound) == true {
- tagName = "<UNKNOWN>"
- } else if err != nil {
- log.Panic(err)
- } else {
- tagName = it.Name
- }
- }
-
- value := tag.Value()
-
- if value.IsIb() == true {
- fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, value.Ib())
- } else {
- fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag)
- }
-
- if isChildIb == true {
- if tag.value.IsIb() == false {
- log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
- }
-
- fmt.Printf("\n")
-
- childIb := tag.value.Ib()
- childIb.printTagTree(levels + 1)
- }
- }
-
- fmt.Printf("\n")
- }
-
- i++
- }
-}
-
-func (ib *IfdBuilder) PrintTagTree() {
- ib.printTagTree(0)
-}
-
-func (ib *IfdBuilder) printIfdTree(levels int) {
- indent := strings.Repeat(" ", levels*2)
-
- i := 0
- for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb {
- prefix := " "
- if i > 0 {
- prefix = ">"
- }
-
- fmt.Printf("%s%s%s\n", indent, prefix, currentIb)
-
- if len(currentIb.tags) > 0 {
- for _, tag := range currentIb.tags {
- isChildIb := false
- _, err := ib.ifdMapping.GetChild(currentIb.ifdPath, tag.tagId)
- if err == nil {
- isChildIb = true
- } else if log.Is(err, ErrChildIfdNotMapped) == false {
- log.Panic(err)
- }
-
- if isChildIb == true {
- if tag.value.IsIb() == false {
- log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag)
- }
-
- childIb := tag.value.Ib()
- childIb.printIfdTree(levels + 1)
- }
- }
- }
-
- i++
- }
-}
-
-func (ib *IfdBuilder) PrintIfdTree() {
- ib.printIfdTree(0)
-}
-
-func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, tagId uint16, lines []string) (linesOutput []string) {
- if lines == nil {
- linesOutput = make([]string, 0)
- } else {
- linesOutput = lines
- }
-
- siblingIfdIndex := 0
- for ; thisIb != nil; thisIb = thisIb.nextIb {
- line := fmt.Sprintf("IFD<PARENTS=[%s] FQ-IFD-PATH=[%s] IFD-INDEX=(%d) IFD-TAG-ID=(0x%04x) TAG=[0x%04x]>", prefix, thisIb.fqIfdPath, siblingIfdIndex, thisIb.ifdTagId, tagId)
- linesOutput = append(linesOutput, line)
-
- for i, tag := range thisIb.tags {
- var childIb *IfdBuilder
- childIfdName := ""
- if tag.value.IsIb() == true {
- childIb = tag.value.Ib()
- childIfdName = childIb.ifdPath
- }
-
- line := fmt.Sprintf("TAG<PARENTS=[%s] FQ-IFD-PATH=[%s] IFD-TAG-ID=(0x%04x) CHILD-IFD=[%s] TAG-INDEX=(%d) TAG=[0x%04x]>", prefix, thisIb.fqIfdPath, thisIb.ifdTagId, childIfdName, i, tag.tagId)
- linesOutput = append(linesOutput, line)
-
- if childIb == nil {
- continue
- }
-
- childPrefix := ""
- if prefix == "" {
- childPrefix = fmt.Sprintf("%s", thisIb.ifdPath)
- } else {
- childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.ifdPath)
- }
-
- linesOutput = thisIb.dumpToStrings(childIb, childPrefix, tag.tagId, linesOutput)
- }
-
- siblingIfdIndex++
- }
-
- return linesOutput
-}
-
-func (ib *IfdBuilder) DumpToStrings() (lines []string) {
- return ib.dumpToStrings(ib, "", 0, lines)
-}
-
-func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ib.nextIb = nextIb
-
- return nil
-}
-
-func (ib *IfdBuilder) DeleteN(tagId uint16, n int) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if n < 1 {
- log.Panicf("N must be at least 1: (%d)", n)
- }
-
- for n > 0 {
- j := -1
- for i, bt := range ib.tags {
- if bt.tagId == tagId {
- j = i
- break
- }
- }
-
- if j == -1 {
- log.Panic(ErrTagEntryNotFound)
- }
-
- ib.tags = append(ib.tags[:j], ib.tags[j+1:]...)
- n--
- }
-
- return nil
-}
-
-func (ib *IfdBuilder) DeleteFirst(tagId uint16) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = ib.DeleteN(tagId, 1)
- log.PanicIf(err)
-
- return nil
-}
-
-func (ib *IfdBuilder) DeleteAll(tagId uint16) (n int, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- for {
- err = ib.DeleteN(tagId, 1)
- if log.Is(err, ErrTagEntryNotFound) == true {
- break
- } else if err != nil {
- log.Panic(err)
- }
-
- n++
- }
-
- return n, nil
-}
-
-func (ib *IfdBuilder) ReplaceAt(position int, bt *BuilderTag) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if position < 0 {
- log.Panicf("replacement position must be 0 or greater")
- } else if position >= len(ib.tags) {
- log.Panicf("replacement position does not exist")
- }
-
- ib.tags[position] = bt
-
- return nil
-}
-
-func (ib *IfdBuilder) Replace(tagId uint16, bt *BuilderTag) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- position, err := ib.Find(tagId)
- log.PanicIf(err)
-
- ib.tags[position] = bt
-
- return nil
-}
-
-// Set will add a new entry or update an existing entry.
-func (ib *IfdBuilder) Set(bt *BuilderTag) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- position, err := ib.Find(bt.tagId)
- if err == nil {
- ib.tags[position] = bt
- } else if log.Is(err, ErrTagEntryNotFound) == true {
- err = ib.add(bt)
- log.PanicIf(err)
- } else {
- log.Panic(err)
- }
-
- return nil
-}
-
-func (ib *IfdBuilder) FindN(tagId uint16, maxFound int) (found []int, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- found = make([]int, 0)
-
- for i, bt := range ib.tags {
- if bt.tagId == tagId {
- found = append(found, i)
- if maxFound == 0 || len(found) >= maxFound {
- break
- }
- }
- }
-
- return found, nil
-}
-
-func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- found, err := ib.FindN(tagId, 1)
- log.PanicIf(err)
-
- if len(found) == 0 {
- log.Panic(ErrTagEntryNotFound)
- }
-
- return found[0], nil
-}
-
-func (ib *IfdBuilder) FindTag(tagId uint16) (bt *BuilderTag, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- found, err := ib.FindN(tagId, 1)
- log.PanicIf(err)
-
- if len(found) == 0 {
- log.Panic(ErrTagEntryNotFound)
- }
-
- position := found[0]
-
- return ib.tags[position], nil
-}
-
-func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- it, err := ib.tagIndex.GetWithName(ib.ifdPath, tagName)
- log.PanicIf(err)
-
- found, err := ib.FindN(it.Id, 1)
- log.PanicIf(err)
-
- if len(found) == 0 {
- log.Panic(ErrTagEntryNotFound)
- }
-
- position := found[0]
-
- return ib.tags[position], nil
-}
-
-func (ib *IfdBuilder) add(bt *BuilderTag) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if bt.ifdPath == "" {
- log.Panicf("BuilderTag ifdPath is not set: %s", bt)
- } else if bt.typeId == 0x0 {
- log.Panicf("BuilderTag type-ID is not set: %s", bt)
- } else if bt.value == nil {
- log.Panicf("BuilderTag value is not set: %s", bt)
- }
-
- ib.tags = append(ib.tags, bt)
- return nil
-}
-
-func (ib *IfdBuilder) Add(bt *BuilderTag) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if bt.value.IsIb() == true {
- log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()")
- }
-
- err = ib.add(bt)
- log.PanicIf(err)
-
- return nil
-}
-
-// AddChildIb adds a tag that branches to a new IFD.
-func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- 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)
- }
-
- // Since no standard IFDs supports occuring more than once, check that a
- // tag of this type has not been previously added. Note that we just search
- // the current IFD and *not every* IFD.
- for _, bt := range childIb.tags {
- if bt.tagId == childIb.ifdTagId {
- log.Panicf("child-IFD already added: %v", childIb.ifdPath)
- }
- }
-
- bt := ib.NewBuilderTagFromBuilder(childIb)
- ib.tags = append(ib.tags, bt)
-
- return nil
-}
-
-func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *BuilderTag) {
- defer func() {
- if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
- }
- }()
-
- value := NewIfdBuilderTagValueFromIfdBuilder(childIb)
-
- bt = NewChildIfdBuilderTag(
- ib.ifdPath,
- childIb.ifdTagId,
- value)
-
- return bt
-}
-
-// AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this
-// builder. It excludes child IFDs. These must be added explicitly via
-// `AddChildIb()`.
-func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, itevr *IfdTagEntryValueResolver, includeTagIds []uint16, excludeTagIds []uint16) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // OBSOLETE(dustin): Support for `itevr` is now obsolete. This parameter will be removed in the future.
-
- thumbnailData, err := ifd.Thumbnail()
- if err == nil {
- err = ib.SetThumbnail(thumbnailData)
- log.PanicIf(err)
- } else if log.Is(err, ErrNoThumbnail) == false {
- log.Panic(err)
- }
-
- for i, ite := range ifd.Entries {
- if ite.TagId == ThumbnailOffsetTagId || ite.TagId == ThumbnailSizeTagId {
- // These will be added on-the-fly when we encode.
- continue
- }
-
- if excludeTagIds != nil && len(excludeTagIds) > 0 {
- found := false
- for _, excludedTagId := range excludeTagIds {
- if excludedTagId == ite.TagId {
- found = true
- }
- }
-
- if found == true {
- continue
- }
- }
-
- if includeTagIds != nil && len(includeTagIds) > 0 {
- // Whether or not there was a list of excludes, if there is a list
- // of includes than the current tag has to be in it.
-
- found := false
- for _, includedTagId := range includeTagIds {
- if includedTagId == ite.TagId {
- found = true
- break
- }
- }
-
- if found == false {
- continue
- }
- }
-
- var bt *BuilderTag
-
- if ite.ChildIfdPath != "" {
- // If we want to add an IFD tag, we'll have to build it first and
- // *then* add it via a different method.
-
- // Figure out which of the child-IFDs that are associated with
- // this IFD represents this specific child IFD.
-
- var childIfd *Ifd
- for _, thisChildIfd := range ifd.Children {
- if thisChildIfd.ParentTagIndex != i {
- continue
- } else if thisChildIfd.TagId != 0xffff && thisChildIfd.TagId != ite.TagId {
- log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex, ite, thisChildIfd)
- }
-
- childIfd = thisChildIfd
- break
- }
-
- if childIfd == nil {
- childTagIds := make([]string, len(ifd.Children))
- for j, childIfd := range ifd.Children {
- childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.TagId, childIfd.ParentTagIndex)
- }
-
- log.Panicf("could not find child IFD for child ITE: IFD-PATH=[%s] TAG-ID=(0x%04x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.IfdPath, ite.TagId, i, childTagIds)
- }
-
- childIb := NewIfdBuilderFromExistingChain(childIfd, nil)
- bt = ib.NewBuilderTagFromBuilder(childIb)
- } else {
- // Non-IFD tag.
-
- valueContext := ifd.GetValueContext(ite)
-
- var rawBytes []byte
-
- if ite.TagType == TypeUndefined {
- // It's an undefined-type value. Try to process, or skip if
- // we don't know how to.
-
- undefinedInterface, err := valueContext.Undefined()
- if err != nil {
- if err == ErrUnhandledUnknownTypedTag {
- // It's an undefined-type tag that we don't handle. If
- // we don't know how to handle it, we can't know how
- // many bytes it is and we must skip it.
- continue
- }
-
- log.Panic(err)
- }
-
- undefined, ok := undefinedInterface.(UnknownTagValue)
- if ok != true {
- log.Panicf("unexpected value returned from undefined-value processor")
- }
-
- rawBytes, err = undefined.ValueBytes()
- log.PanicIf(err)
- } else {
- // It's a value with a standard type.
-
- var err error
-
- rawBytes, err = valueContext.readRawEncoded()
- log.PanicIf(err)
- }
-
- value := NewIfdBuilderTagValueFromBytes(rawBytes)
-
- bt = NewBuilderTag(
- ifd.IfdPath,
- ite.TagId,
- ite.TagType,
- value,
- ib.byteOrder)
- }
-
- err := ib.add(bt)
- log.PanicIf(err)
- }
-
- return nil
-}
-
-// AddStandard quickly and easily composes and adds the tag using the
-// information already known about a tag. Only works with standard tags.
-func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- it, err := ib.tagIndex.Get(ib.ifdPath, tagId)
- log.PanicIf(err)
-
- bt := NewStandardBuilderTag(ib.ifdPath, it, ib.byteOrder, value)
-
- err = ib.add(bt)
- log.PanicIf(err)
-
- return nil
-}
-
-// AddStandardWithName quickly and easily composes and adds the tag using the
-// information already known about a tag (using the name). Only works with
-// standard tags.
-func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- it, err := ib.tagIndex.GetWithName(ib.ifdPath, tagName)
- log.PanicIf(err)
-
- bt := NewStandardBuilderTag(ib.ifdPath, it, ib.byteOrder, value)
-
- err = ib.add(bt)
- log.PanicIf(err)
-
- return nil
-}
-
-// SetStandard quickly and easily composes and adds or replaces the tag using
-// the information already known about a tag. Only works with standard tags.
-func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Add test for this function.
-
- it, err := ib.tagIndex.Get(ib.ifdPath, tagId)
- log.PanicIf(err)
-
- bt := NewStandardBuilderTag(ib.ifdPath, it, ib.byteOrder, value)
-
- i, err := ib.Find(tagId)
- if err != nil {
- if log.Is(err, ErrTagEntryNotFound) == false {
- log.Panic(err)
- }
-
- ib.tags = append(ib.tags, bt)
- } else {
- ib.tags[i] = bt
- }
-
- return nil
-}
-
-// SetStandardWithName quickly and easily composes and adds or replaces the
-// tag using the information already known about a tag (using the name). Only
-// works with standard tags.
-func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Add test for this function.
-
- it, err := ib.tagIndex.GetWithName(ib.ifdPath, tagName)
- log.PanicIf(err)
-
- bt := NewStandardBuilderTag(ib.ifdPath, it, ib.byteOrder, value)
-
- i, err := ib.Find(bt.tagId)
- if err != nil {
- if log.Is(err, ErrTagEntryNotFound) == false {
- log.Panic(err)
- }
-
- ib.tags = append(ib.tags, bt)
- } else {
- ib.tags[i] = bt
- }
-
- return nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/ifd_builder_encode.go b/vendor/github.com/dsoprea/go-exif/ifd_builder_encode.go
deleted file mode 100644
index 90fb2ddbf..000000000
--- a/vendor/github.com/dsoprea/go-exif/ifd_builder_encode.go
+++ /dev/null
@@ -1,530 +0,0 @@
-package exif
-
-import (
- "bytes"
- "fmt"
- "strings"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-const (
- // Tag-ID + Tag-Type + Unit-Count + Value/Offset.
- IfdTagEntrySize = uint32(2 + 2 + 4 + 4)
-)
-
-type ByteWriter struct {
- b *bytes.Buffer
- byteOrder binary.ByteOrder
-}
-
-func NewByteWriter(b *bytes.Buffer, byteOrder binary.ByteOrder) (bw *ByteWriter) {
- return &ByteWriter{
- b: b,
- byteOrder: byteOrder,
- }
-}
-
-func (bw ByteWriter) writeAsBytes(value interface{}) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = binary.Write(bw.b, bw.byteOrder, value)
- log.PanicIf(err)
-
- return nil
-}
-
-func (bw ByteWriter) WriteUint32(value uint32) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = bw.writeAsBytes(value)
- log.PanicIf(err)
-
- return nil
-}
-
-func (bw ByteWriter) WriteUint16(value uint16) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = bw.writeAsBytes(value)
- log.PanicIf(err)
-
- return nil
-}
-
-func (bw ByteWriter) WriteFourBytes(value []byte) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- len_ := len(value)
- if len_ != 4 {
- log.Panicf("value is not four-bytes: (%d)", len_)
- }
-
- _, err = bw.b.Write(value)
- log.PanicIf(err)
-
- return nil
-}
-
-// ifdOffsetIterator keeps track of where the next IFD should be written by
-// keeping track of where the offsets start, the data that has been added, and
-// bumping the offset *when* the data is added.
-type ifdDataAllocator struct {
- offset uint32
- b bytes.Buffer
-}
-
-func newIfdDataAllocator(ifdDataAddressableOffset uint32) *ifdDataAllocator {
- return &ifdDataAllocator{
- offset: ifdDataAddressableOffset,
- }
-}
-
-func (ida *ifdDataAllocator) Allocate(value []byte) (offset uint32, err error) {
- _, err = ida.b.Write(value)
- log.PanicIf(err)
-
- offset = ida.offset
- ida.offset += uint32(len(value))
-
- return offset, nil
-}
-
-func (ida *ifdDataAllocator) NextOffset() uint32 {
- return ida.offset
-}
-
-func (ida *ifdDataAllocator) Bytes() []byte {
- return ida.b.Bytes()
-}
-
-// IfdByteEncoder converts an IB to raw bytes (for writing) while also figuring
-// out all of the allocations and indirection that is required for extended
-// data.
-type IfdByteEncoder struct {
- // journal holds a list of actions taken while encoding.
- journal [][3]string
-}
-
-func NewIfdByteEncoder() (ibe *IfdByteEncoder) {
- return &IfdByteEncoder{
- journal: make([][3]string, 0),
- }
-}
-
-func (ibe *IfdByteEncoder) Journal() [][3]string {
- return ibe.journal
-}
-
-func (ibe *IfdByteEncoder) TableSize(entryCount int) uint32 {
- // Tag-Count + (Entry-Size * Entry-Count) + Next-IFD-Offset.
- return uint32(2) + (IfdTagEntrySize * uint32(entryCount)) + uint32(4)
-}
-
-func (ibe *IfdByteEncoder) pushToJournal(where, direction, format string, args ...interface{}) {
- event := [3]string{
- direction,
- where,
- fmt.Sprintf(format, args...),
- }
-
- ibe.journal = append(ibe.journal, event)
-}
-
-// PrintJournal prints a hierarchical representation of the steps taken during
-// encoding.
-func (ibe *IfdByteEncoder) PrintJournal() {
- maxWhereLength := 0
- for _, event := range ibe.journal {
- where := event[1]
-
- len_ := len(where)
- if len_ > maxWhereLength {
- maxWhereLength = len_
- }
- }
-
- level := 0
- for i, event := range ibe.journal {
- direction := event[0]
- where := event[1]
- message := event[2]
-
- if direction != ">" && direction != "<" && direction != "-" {
- log.Panicf("journal operation not valid: [%s]", direction)
- }
-
- if direction == "<" {
- if level <= 0 {
- log.Panicf("journal operations unbalanced (too many closes)")
- }
-
- level--
- }
-
- indent := strings.Repeat(" ", level)
-
- fmt.Printf("%3d %s%s %s: %s\n", i, indent, direction, where, message)
-
- if direction == ">" {
- level++
- }
- }
-
- if level != 0 {
- log.Panicf("journal operations unbalanced (too many opens)")
- }
-}
-
-// encodeTagToBytes encodes the given tag to a byte stream. If
-// `nextIfdOffsetToWrite` is more than (0), recurse into child IFDs
-// (`nextIfdOffsetToWrite` is required in order for them to know where the its
-// IFD data will be written, in order for them to know the offset of where
-// their allocated-data block will start, which follows right behind).
-func (ibe *IfdByteEncoder) encodeTagToBytes(ib *IfdBuilder, bt *BuilderTag, bw *ByteWriter, ida *ifdDataAllocator, nextIfdOffsetToWrite uint32) (childIfdBlock []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // Write tag-ID.
- err = bw.WriteUint16(bt.tagId)
- log.PanicIf(err)
-
- // Works for both values and child IFDs (which have an official size of
- // LONG).
- err = bw.WriteUint16(uint16(bt.typeId))
- log.PanicIf(err)
-
- // Write unit-count.
-
- if bt.value.IsBytes() == true {
- effectiveType := bt.typeId
- if bt.typeId == TypeUndefined {
- effectiveType = TypeByte
- }
-
- // It's a non-unknown value.Calculate the count of values of
- // the type that we're writing and the raw bytes for the whole list.
-
- typeSize := uint32(effectiveType.Size())
-
- valueBytes := bt.value.Bytes()
-
- len_ := len(valueBytes)
- unitCount := uint32(len_) / typeSize
-
- if _, found := tagsWithoutAlignment[bt.tagId]; found == false {
- remainder := uint32(len_) % typeSize
-
- if remainder > 0 {
- log.Panicf("tag (0x%04x) value of (%d) bytes not evenly divisible by type-size (%d)", bt.tagId, len_, typeSize)
- }
- }
-
- err = bw.WriteUint32(unitCount)
- log.PanicIf(err)
-
- // Write four-byte value/offset.
-
- if len_ > 4 {
- offset, err := ida.Allocate(valueBytes)
- log.PanicIf(err)
-
- err = bw.WriteUint32(offset)
- log.PanicIf(err)
- } else {
- fourBytes := make([]byte, 4)
- copy(fourBytes, valueBytes)
-
- err = bw.WriteFourBytes(fourBytes)
- log.PanicIf(err)
- }
- } else {
- if bt.value.IsIb() == false {
- log.Panicf("tag value is not a byte-slice but also not a child IB: %v", bt)
- }
-
- // Write unit-count (one LONG representing one offset).
- err = bw.WriteUint32(1)
- log.PanicIf(err)
-
- if nextIfdOffsetToWrite > 0 {
- var err error
-
- ibe.pushToJournal("encodeTagToBytes", ">", "[%s]->[%s]", ib.ifdPath, bt.value.Ib().ifdPath)
-
- // Create the block of IFD data and everything it requires.
- childIfdBlock, err = ibe.encodeAndAttachIfd(bt.value.Ib(), nextIfdOffsetToWrite)
- log.PanicIf(err)
-
- ibe.pushToJournal("encodeTagToBytes", "<", "[%s]->[%s]", bt.value.Ib().ifdPath, ib.ifdPath)
-
- // Use the next-IFD offset for it. The IFD will actually get
- // attached after we return.
- err = bw.WriteUint32(nextIfdOffsetToWrite)
- log.PanicIf(err)
-
- } else {
- // No child-IFDs are to be allocated. Finish the entry with a NULL
- // pointer.
-
- ibe.pushToJournal("encodeTagToBytes", "-", "*Not* descending to child: [%s]", bt.value.Ib().ifdPath)
-
- err = bw.WriteUint32(0)
- log.PanicIf(err)
- }
- }
-
- return childIfdBlock, nil
-}
-
-// encodeIfdToBytes encodes the given IB to a byte-slice. We are given the
-// offset at which this IFD will be written. This method is used called both to
-// pre-determine how big the table is going to be (so that we can calculate the
-// address to allocate data at) as well as to write the final table.
-//
-// It is necessary to fully realize the table in order to predetermine its size
-// because it is not enough to know the size of the table: If there are child
-// IFDs, we will not be able to allocate them without first knowing how much
-// data we need to allocate for the current IFD.
-func (ibe *IfdByteEncoder) encodeIfdToBytes(ib *IfdBuilder, ifdAddressableOffset uint32, nextIfdOffsetToWrite uint32, setNextIb bool) (data []byte, tableSize uint32, dataSize uint32, childIfdSizes []uint32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ibe.pushToJournal("encodeIfdToBytes", ">", "%s", ib)
-
- tableSize = ibe.TableSize(len(ib.tags))
-
- b := new(bytes.Buffer)
- bw := NewByteWriter(b, ib.byteOrder)
-
- // Write tag count.
- err = bw.WriteUint16(uint16(len(ib.tags)))
- log.PanicIf(err)
-
- ida := newIfdDataAllocator(ifdAddressableOffset)
-
- childIfdBlocks := make([][]byte, 0)
-
- // Write raw bytes for each tag entry. Allocate larger data to be referred
- // to in the follow-up data-block as required. Any "unknown"-byte tags that
- // we can't parse will not be present here (using AddTagsFromExisting(), at
- // least).
- for _, bt := range ib.tags {
- childIfdBlock, err := ibe.encodeTagToBytes(ib, bt, bw, ida, nextIfdOffsetToWrite)
- log.PanicIf(err)
-
- if childIfdBlock != nil {
- // We aren't allowed to have non-nil child IFDs if we're just
- // sizing things up.
- if nextIfdOffsetToWrite == 0 {
- log.Panicf("no IFD offset provided for child-IFDs; no new child-IFDs permitted")
- }
-
- nextIfdOffsetToWrite += uint32(len(childIfdBlock))
- childIfdBlocks = append(childIfdBlocks, childIfdBlock)
- }
- }
-
- dataBytes := ida.Bytes()
- dataSize = uint32(len(dataBytes))
-
- childIfdSizes = make([]uint32, len(childIfdBlocks))
- childIfdsTotalSize := uint32(0)
- for i, childIfdBlock := range childIfdBlocks {
- len_ := uint32(len(childIfdBlock))
- childIfdSizes[i] = len_
- childIfdsTotalSize += len_
- }
-
- // N the link from this IFD to the next IFD that will be written in the
- // next cycle.
- if setNextIb == true {
- // Write address of next IFD in chain. This will be the original
- // allocation offset plus the size of everything we have allocated for
- // this IFD and its child-IFDs.
- //
- // It is critical that this number is stepped properly. We experienced
- // an issue whereby it first looked like we were duplicating the IFD and
- // then that we were duplicating the tags in the wrong IFD, and then
- // finally we determined that the next-IFD offset for the first IFD was
- // accidentally pointing back to the EXIF IFD, so we were visiting it
- // twice when visiting through the tags after decoding. It was an
- // expensive bug to find.
-
- ibe.pushToJournal("encodeIfdToBytes", "-", "Setting 'next' IFD to (0x%08x).", nextIfdOffsetToWrite)
-
- err := bw.WriteUint32(nextIfdOffsetToWrite)
- log.PanicIf(err)
- } else {
- err := bw.WriteUint32(0)
- log.PanicIf(err)
- }
-
- _, err = b.Write(dataBytes)
- log.PanicIf(err)
-
- // Append any child IFD blocks after our table and data blocks. These IFDs
- // were equipped with the appropriate offset information so it's expected
- // that all offsets referred to by these will be correct.
- //
- // Note that child-IFDs are append after the current IFD and before the
- // next IFD, as opposed to the root IFDs, which are chained together but
- // will be interrupted by these child-IFDs (which is expected, per the
- // standard).
-
- for _, childIfdBlock := range childIfdBlocks {
- _, err = b.Write(childIfdBlock)
- log.PanicIf(err)
- }
-
- ibe.pushToJournal("encodeIfdToBytes", "<", "%s", ib)
-
- return b.Bytes(), tableSize, dataSize, childIfdSizes, nil
-}
-
-// encodeAndAttachIfd is a reentrant function that processes the IFD chain.
-func (ibe *IfdByteEncoder) encodeAndAttachIfd(ib *IfdBuilder, ifdAddressableOffset uint32) (data []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ibe.pushToJournal("encodeAndAttachIfd", ">", "%s", ib)
-
- b := new(bytes.Buffer)
-
- i := 0
-
- for thisIb := ib; thisIb != nil; thisIb = thisIb.nextIb {
-
- // Do a dry-run in order to pre-determine its size requirement.
-
- ibe.pushToJournal("encodeAndAttachIfd", ">", "Beginning encoding process: (%d) [%s]", i, thisIb.ifdPath)
-
- ibe.pushToJournal("encodeAndAttachIfd", ">", "Calculating size: (%d) [%s]", i, thisIb.ifdPath)
-
- _, tableSize, allocatedDataSize, _, err := ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, 0, false)
- log.PanicIf(err)
-
- ibe.pushToJournal("encodeAndAttachIfd", "<", "Finished calculating size: (%d) [%s]", i, thisIb.ifdPath)
-
- ifdAddressableOffset += tableSize
- nextIfdOffsetToWrite := ifdAddressableOffset + allocatedDataSize
-
- ibe.pushToJournal("encodeAndAttachIfd", ">", "Next IFD will be written at offset (0x%08x)", nextIfdOffsetToWrite)
-
- // Write our IFD as well as any child-IFDs (now that we know the offset
- // where new IFDs and their data will be allocated).
-
- setNextIb := thisIb.nextIb != nil
-
- ibe.pushToJournal("encodeAndAttachIfd", ">", "Encoding starting: (%d) [%s] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, thisIb.ifdPath, nextIfdOffsetToWrite)
-
- tableAndAllocated, effectiveTableSize, effectiveAllocatedDataSize, childIfdSizes, err :=
- ibe.encodeIfdToBytes(thisIb, ifdAddressableOffset, nextIfdOffsetToWrite, setNextIb)
-
- log.PanicIf(err)
-
- if effectiveTableSize != tableSize {
- log.Panicf("written table size does not match the pre-calculated table size: (%d) != (%d) %s", effectiveTableSize, tableSize, ib)
- } else if effectiveAllocatedDataSize != allocatedDataSize {
- log.Panicf("written allocated-data size does not match the pre-calculated allocated-data size: (%d) != (%d) %s", effectiveAllocatedDataSize, allocatedDataSize, ib)
- }
-
- ibe.pushToJournal("encodeAndAttachIfd", "<", "Encoding done: (%d) [%s]", i, thisIb.ifdPath)
-
- totalChildIfdSize := uint32(0)
- for _, childIfdSize := range childIfdSizes {
- totalChildIfdSize += childIfdSize
- }
-
- if len(tableAndAllocated) != int(tableSize+allocatedDataSize+totalChildIfdSize) {
- log.Panicf("IFD table and data is not a consistent size: (%d) != (%d)", len(tableAndAllocated), tableSize+allocatedDataSize+totalChildIfdSize)
- }
-
- // TODO(dustin): We might want to verify the original tableAndAllocated length, too.
-
- _, err = b.Write(tableAndAllocated)
- log.PanicIf(err)
-
- // Advance past what we've allocated, thus far.
-
- ifdAddressableOffset += allocatedDataSize + totalChildIfdSize
-
- ibe.pushToJournal("encodeAndAttachIfd", "<", "Finishing encoding process: (%d) [%s] [FINAL:] NEXT-IFD-OFFSET-TO-WRITE=(0x%08x)", i, ib.ifdPath, nextIfdOffsetToWrite)
-
- i++
- }
-
- ibe.pushToJournal("encodeAndAttachIfd", "<", "%s", ib)
-
- return b.Bytes(), nil
-}
-
-// EncodeToExifPayload is the base encoding step that transcribes the entire IB
-// structure to its on-disk layout.
-func (ibe *IfdByteEncoder) EncodeToExifPayload(ib *IfdBuilder) (data []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- data, err = ibe.encodeAndAttachIfd(ib, ExifDefaultFirstIfdOffset)
- log.PanicIf(err)
-
- return data, nil
-}
-
-// EncodeToExif calls EncodeToExifPayload and then packages the result into a
-// complete EXIF block.
-func (ibe *IfdByteEncoder) EncodeToExif(ib *IfdBuilder) (data []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- encodedIfds, err := ibe.EncodeToExifPayload(ib)
- log.PanicIf(err)
-
- // Wrap the IFD in a formal EXIF block.
-
- b := new(bytes.Buffer)
-
- headerBytes, err := BuildExifHeader(ib.byteOrder, ExifDefaultFirstIfdOffset)
- log.PanicIf(err)
-
- _, err = b.Write(headerBytes)
- log.PanicIf(err)
-
- _, err = b.Write(encodedIfds)
- log.PanicIf(err)
-
- return b.Bytes(), nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/ifd_enumerate.go b/vendor/github.com/dsoprea/go-exif/ifd_enumerate.go
deleted file mode 100644
index 317e847a9..000000000
--- a/vendor/github.com/dsoprea/go-exif/ifd_enumerate.go
+++ /dev/null
@@ -1,1356 +0,0 @@
-package exif
-
-import (
- "bytes"
- "errors"
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "time"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-var (
- ifdEnumerateLogger = log.NewLogger("exifjpeg.ifd")
-)
-
-var (
- ErrNoThumbnail = errors.New("no thumbnail")
- ErrNoGpsTags = errors.New("no gps tags")
- ErrTagTypeNotValid = errors.New("tag type invalid")
-)
-
-var (
- ValidGpsVersions = [][4]byte{
- {2, 2, 0, 0},
-
- // Suddenly appeared at the default in 2.31: https://home.jeita.or.jp/tsc/std-pdf/CP-3451D.pdf
- //
- // Note that the presence of 2.3.0.0 doesn't seem to guarantee
- // coordinates. In some cases, we seen just the following:
- //
- // GPS Tag Version |2.3.0.0
- // GPS Receiver Status |V
- // Geodetic Survey Data|WGS-84
- // GPS Differential Cor|0
- //
- {2, 3, 0, 0},
- }
-)
-
-// IfdTagEnumerator knows how to decode an IFD and all of the tags it
-// describes.
-//
-// The IFDs and the actual values can float throughout the EXIF block, but the
-// IFD itself is just a minor header followed by a set of repeating,
-// statically-sized records. So, the tags (though notnecessarily their values)
-// are fairly simple to enumerate.
-type IfdTagEnumerator struct {
- byteOrder binary.ByteOrder
- addressableData []byte
- ifdOffset uint32
- buffer *bytes.Buffer
-}
-
-func NewIfdTagEnumerator(addressableData []byte, byteOrder binary.ByteOrder, ifdOffset uint32) (ite *IfdTagEnumerator) {
- ite = &IfdTagEnumerator{
- addressableData: addressableData,
- byteOrder: byteOrder,
- buffer: bytes.NewBuffer(addressableData[ifdOffset:]),
- }
-
- return ite
-}
-
-// getUint16 reads a uint16 and advances both our current and our current
-// accumulator (which allows us to know how far to seek to the beginning of the
-// next IFD when it's time to jump).
-func (ife *IfdTagEnumerator) getUint16() (value uint16, raw []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- needBytes := 2
- offset := 0
- raw = make([]byte, needBytes)
-
- for offset < needBytes {
- n, err := ife.buffer.Read(raw[offset:])
- log.PanicIf(err)
-
- offset += n
- }
-
- value = ife.byteOrder.Uint16(raw)
-
- return value, raw, nil
-}
-
-// getUint32 reads a uint32 and advances both our current and our current
-// accumulator (which allows us to know how far to seek to the beginning of the
-// next IFD when it's time to jump).
-func (ife *IfdTagEnumerator) getUint32() (value uint32, raw []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- needBytes := 4
- offset := 0
- raw = make([]byte, needBytes)
-
- for offset < needBytes {
- n, err := ife.buffer.Read(raw[offset:])
- log.PanicIf(err)
-
- offset += n
- }
-
- value = ife.byteOrder.Uint32(raw)
-
- return value, raw, nil
-}
-
-type IfdEnumerate struct {
- exifData []byte
- buffer *bytes.Buffer
- byteOrder binary.ByteOrder
- currentOffset uint32
- tagIndex *TagIndex
- ifdMapping *IfdMapping
-}
-
-func NewIfdEnumerate(ifdMapping *IfdMapping, tagIndex *TagIndex, exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate {
- return &IfdEnumerate{
- exifData: exifData,
- buffer: bytes.NewBuffer(exifData),
- byteOrder: byteOrder,
- ifdMapping: ifdMapping,
- tagIndex: tagIndex,
- }
-}
-
-func (ie *IfdEnumerate) getTagEnumerator(ifdOffset uint32) (ite *IfdTagEnumerator) {
- ite = NewIfdTagEnumerator(
- ie.exifData[ExifAddressableAreaStart:],
- ie.byteOrder,
- ifdOffset)
-
- return ite
-}
-
-func (ie *IfdEnumerate) parseTag(fqIfdPath string, tagPosition int, ite *IfdTagEnumerator, resolveValue bool) (tag *IfdTagEntry, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- tagId, _, err := ite.getUint16()
- log.PanicIf(err)
-
- tagTypeRaw, _, err := ite.getUint16()
- log.PanicIf(err)
-
- tagType := TagTypePrimitive(tagTypeRaw)
-
- unitCount, _, err := ite.getUint32()
- log.PanicIf(err)
-
- valueOffset, rawValueOffset, err := ite.getUint32()
- log.PanicIf(err)
-
- if _, found := TypeNames[tagType]; found == false {
- log.Panic(ErrTagTypeNotValid)
- }
-
- ifdPath, err := ie.ifdMapping.StripPathPhraseIndices(fqIfdPath)
- log.PanicIf(err)
-
- tag = &IfdTagEntry{
- IfdPath: ifdPath,
- TagId: tagId,
- TagIndex: tagPosition,
- TagType: tagType,
- UnitCount: unitCount,
- ValueOffset: valueOffset,
- RawValueOffset: rawValueOffset,
- }
-
- if resolveValue == true {
- value, isUnhandledUnknown, err := ie.resolveTagValue(tag)
- log.PanicIf(err)
-
- tag.value = value
- tag.isUnhandledUnknown = isUnhandledUnknown
- }
-
- // If it's an IFD but not a standard one, it'll just be seen as a LONG
- // (the standard IFD tag type), later, unless we skip it because it's
- // [likely] not even in the standard list of known tags.
- mi, err := ie.ifdMapping.GetChild(ifdPath, tagId)
- if err == nil {
- tag.ChildIfdName = mi.Name
- tag.ChildIfdPath = mi.PathPhrase()
- tag.ChildFqIfdPath = fmt.Sprintf("%s/%s", fqIfdPath, mi.Name)
-
- // We also need to set `tag.ChildFqIfdPath` but can't do it here
- // because we don't have the IFD index.
- } else if log.Is(err, ErrChildIfdNotMapped) == false {
- log.Panic(err)
- }
-
- return tag, nil
-}
-
-func (ie *IfdEnumerate) GetValueContext(ite *IfdTagEntry) *ValueContext {
-
- // TODO(dustin): Add test
-
- addressableData := ie.exifData[ExifAddressableAreaStart:]
-
- return newValueContextFromTag(
- ite,
- addressableData,
- ie.byteOrder)
-}
-
-func (ie *IfdEnumerate) resolveTagValue(ite *IfdTagEntry) (valueBytes []byte, isUnhandledUnknown bool, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- addressableData := ie.exifData[ExifAddressableAreaStart:]
-
- // Return the exact bytes of the unknown-type value. Returning a string
- // (`ValueString`) is easy because we can just pass everything to
- // `Sprintf()`. Returning the raw, typed value (`Value`) is easy
- // (obviously). However, here, in order to produce the list of bytes, we
- // need to coerce whatever `Undefined()` returns.
- if ite.TagType == TypeUndefined {
- valueContext := ie.GetValueContext(ite)
-
- value, err := valueContext.Undefined()
- if err != nil {
- if err == ErrUnhandledUnknownTypedTag {
- valueBytes = []byte(UnparseableUnknownTagValuePlaceholder)
- return valueBytes, true, nil
- }
-
- log.Panic(err)
- } else {
- switch value.(type) {
- case []byte:
- return value.([]byte), false, nil
- case TagUnknownType_UnknownValue:
- b := []byte(value.(TagUnknownType_UnknownValue))
- return b, false, nil
- case string:
- return []byte(value.(string)), false, nil
- case UnknownTagValue:
- valueBytes, err := value.(UnknownTagValue).ValueBytes()
- log.PanicIf(err)
-
- return valueBytes, false, 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%04x) (1): [%s]", ite.TagId, reflect.TypeOf(value))
- }
- }
- } else {
- originalType := NewTagType(ite.TagType, ie.byteOrder)
- byteCount := uint32(originalType.Type().Size()) * ite.UnitCount
-
- tt := NewTagType(TypeByte, ie.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.
- valueBytes, err = tt.ParseBytes(ite.RawValueOffset, byteCount)
- log.PanicIf(err)
- } else {
- iteLogger.Debugf(nil, "Reading BYTE value (ITE; at offset).")
-
- valueBytes, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount)
- log.PanicIf(err)
- }
- }
-
- return valueBytes, false, nil
-}
-
-// RawTagVisitorPtr 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).
-//
-// This was reimplemented as an interface to allow for simpler change management
-// in the future.
-type RawTagWalk interface {
- Visit(fqIfdPath string, ifdIndex int, tagId uint16, tagType TagType, valueContext *ValueContext) (err error)
-}
-
-type RawTagWalkLegacyWrapper struct {
- legacyVisitor RawTagVisitor
-}
-
-func (rtwlw RawTagWalkLegacyWrapper) Visit(fqIfdPath string, ifdIndex int, tagId uint16, tagType TagType, valueContext *ValueContext) (err error) {
- return rtwlw.legacyVisitor(fqIfdPath, ifdIndex, tagId, tagType, *valueContext)
-}
-
-// RawTagVisitor 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).
-//
-// DEPRECATED(dustin): Use a RawTagWalk instead.
-type RawTagVisitor func(fqIfdPath string, ifdIndex int, tagId uint16, tagType TagType, valueContext ValueContext) (err error)
-
-// ParseIfd decodes the IFD block that we're currently sitting on the first
-// byte of.
-func (ie *IfdEnumerate) ParseIfd(fqIfdPath string, ifdIndex int, ite *IfdTagEnumerator, visitor interface{}, doDescend bool, resolveValues bool) (nextIfdOffset uint32, entries []*IfdTagEntry, thumbnailData []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- var visitorWrapper RawTagWalk
-
- if visitor != nil {
- var ok bool
-
- visitorWrapper, ok = visitor.(RawTagWalk)
- if ok == false {
- // Legacy usage.
-
- // `ok` can be `true` but `legacyVisitor` can still be `nil` (when
- // passed as nil).
- if legacyVisitor, ok := visitor.(RawTagVisitor); ok == true && legacyVisitor != nil {
- visitorWrapper = RawTagWalkLegacyWrapper{
- legacyVisitor: legacyVisitor,
- }
- }
- }
- }
-
- tagCount, _, err := ite.getUint16()
- log.PanicIf(err)
-
- ifdEnumerateLogger.Debugf(nil, "Current IFD tag-count: (%d)", tagCount)
-
- entries = make([]*IfdTagEntry, 0)
-
- var iteThumbnailOffset *IfdTagEntry
- var iteThumbnailSize *IfdTagEntry
-
- for i := 0; i < int(tagCount); i++ {
- tag, err := ie.parseTag(fqIfdPath, i, ite, resolveValues)
- if err != nil {
- if log.Is(err, ErrTagTypeNotValid) == true {
- ifdEnumerateLogger.Warningf(nil, "Tag in IFD [%s] at position (%d) has invalid type and will be skipped.", fqIfdPath, i)
- continue
- }
-
- log.Panic(err)
- }
-
- if tag.TagId == ThumbnailOffsetTagId {
- iteThumbnailOffset = tag
-
- continue
- } else if tag.TagId == ThumbnailSizeTagId {
- iteThumbnailSize = tag
- continue
- }
-
- if visitorWrapper != nil {
- tt := NewTagType(tag.TagType, ie.byteOrder)
-
- valueContext := ie.GetValueContext(tag)
-
- err := visitorWrapper.Visit(fqIfdPath, ifdIndex, tag.TagId, tt, valueContext)
- log.PanicIf(err)
- }
-
- // If it's an IFD but not a standard one, it'll just be seen as a LONG
- // (the standard IFD tag type), later, unless we skip it because it's
- // [likely] not even in the standard list of known tags.
- if tag.ChildIfdPath != "" {
- if doDescend == true {
- ifdEnumerateLogger.Debugf(nil, "Descending to IFD [%s].", tag.ChildIfdPath)
-
- err := ie.scan(tag.ChildFqIfdPath, tag.ValueOffset, visitor, resolveValues)
- log.PanicIf(err)
- }
- }
-
- entries = append(entries, tag)
- }
-
- if iteThumbnailOffset != nil && iteThumbnailSize != nil {
- thumbnailData, err = ie.parseThumbnail(iteThumbnailOffset, iteThumbnailSize)
- log.PanicIf(err)
- }
-
- nextIfdOffset, _, err = ite.getUint32()
- log.PanicIf(err)
-
- ifdEnumerateLogger.Debugf(nil, "Next IFD at offset: (%08x)", nextIfdOffset)
-
- return nextIfdOffset, entries, thumbnailData, nil
-}
-
-func (ie *IfdEnumerate) parseThumbnail(offsetIte, lengthIte *IfdTagEntry) (thumbnailData []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- addressableData := ie.exifData[ExifAddressableAreaStart:]
-
- vRaw, err := lengthIte.Value(addressableData, ie.byteOrder)
- log.PanicIf(err)
-
- vList := vRaw.([]uint32)
- if len(vList) != 1 {
- log.Panicf("not exactly one long: (%d)", len(vList))
- }
-
- length := vList[0]
-
- // The tag is official a LONG type, but it's actually an offset to a blob of bytes.
- offsetIte.TagType = TypeByte
- offsetIte.UnitCount = length
-
- thumbnailData, err = offsetIte.ValueBytes(addressableData, ie.byteOrder)
- log.PanicIf(err)
-
- return thumbnailData, nil
-}
-
-// Scan enumerates the different EXIF's IFD blocks.
-func (ie *IfdEnumerate) scan(fqIfdName string, ifdOffset uint32, visitor interface{}, resolveValues bool) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- for ifdIndex := 0; ; ifdIndex++ {
- ifdEnumerateLogger.Debugf(nil, "Parsing IFD [%s] (%d) at offset (%04x).", fqIfdName, ifdIndex, ifdOffset)
- ite := ie.getTagEnumerator(ifdOffset)
-
- nextIfdOffset, _, _, err := ie.ParseIfd(fqIfdName, ifdIndex, ite, visitor, true, resolveValues)
- log.PanicIf(err)
-
- if nextIfdOffset == 0 {
- break
- }
-
- ifdOffset = nextIfdOffset
- }
-
- return nil
-}
-
-// Scan enumerates the different EXIF blocks (called IFDs). `rootIfdName` will
-// be "IFD" in the TIFF standard.
-func (ie *IfdEnumerate) Scan(rootIfdName string, ifdOffset uint32, visitor RawTagVisitor, resolveValue bool) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = ie.scan(rootIfdName, ifdOffset, visitor, resolveValue)
- log.PanicIf(err)
-
- return nil
-}
-
-// Ifd represents a single parsed IFD.
-type Ifd struct {
-
- // TODO(dustin): !! Why are all of these public? Privatize them and then add NextIfd().
-
- // This is just for convenience, just so that we can easily get the values
- // and not involve other projects in semantics that they won't otherwise
- // need to know.
- addressableData []byte
-
- ByteOrder binary.ByteOrder
-
- // Name is the name of the IFD (the rightmost name in the path, sans any
- // indices).
- Name string
-
- // IfdPath is a simple IFD path (e.g. IFD/GPSInfo). No indices.
- IfdPath string
-
- // FqIfdPath is a fully-qualified IFD path (e.g. IFD0/GPSInfo0). With
- // indices.
- FqIfdPath string
-
- TagId uint16
-
- Id int
-
- ParentIfd *Ifd
-
- // ParentTagIndex is our tag position in the parent IFD, if we had a parent
- // (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
- // instead of as a child).
- ParentTagIndex int
-
- // Name string
- Index int
- Offset uint32
-
- Entries []*IfdTagEntry
- EntriesByTagId map[uint16][]*IfdTagEntry
-
- Children []*Ifd
-
- ChildIfdIndex map[string]*Ifd
-
- NextIfdOffset uint32
- NextIfd *Ifd
-
- thumbnailData []byte
-
- ifdMapping *IfdMapping
- tagIndex *TagIndex
-}
-
-func (ifd *Ifd) ChildWithIfdPath(ifdPath string) (childIfd *Ifd, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- for _, childIfd := range ifd.Children {
- if childIfd.IfdPath == ifdPath {
- return childIfd, nil
- }
- }
-
- log.Panic(ErrTagNotFound)
- return nil, nil
-}
-
-func (ifd *Ifd) TagValue(ite *IfdTagEntry) (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- value, err = ite.Value(ifd.addressableData, ifd.ByteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (ifd *Ifd) TagValueBytes(ite *IfdTagEntry) (value []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- value, err = ite.ValueBytes(ifd.addressableData, ifd.ByteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-// FindTagWithId returns a list of tags (usually just zero or one) that match
-// the given tag ID. This is efficient.
-func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- results, found := ifd.EntriesByTagId[tagId]
- if found != true {
- log.Panic(ErrTagNotFound)
- }
-
- return results, nil
-}
-
-// FindTagWithName returns a list of tags (usually just zero or one) that match
-// the given tag name. This is not efficient (though the labor is trivial).
-func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- it, err := ifd.tagIndex.GetWithName(ifd.IfdPath, tagName)
- if log.Is(err, ErrTagNotFound) == true {
- log.Panic(ErrTagNotStandard)
- } else if err != nil {
- log.Panic(err)
- }
-
- results = make([]*IfdTagEntry, 0)
- for _, ite := range ifd.Entries {
- if ite.TagId == it.Id {
- results = append(results, ite)
- }
- }
-
- if len(results) == 0 {
- log.Panic(ErrTagNotFound)
- }
-
- return results, nil
-}
-
-func (ifd Ifd) String() string {
- parentOffset := uint32(0)
- if ifd.ParentIfd != nil {
- parentOffset = ifd.ParentIfd.Offset
- }
-
- return fmt.Sprintf("Ifd<ID=(%d) IFD-PATH=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.Id, ifd.IfdPath, ifd.Index, len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset)
-}
-
-func (ifd *Ifd) Thumbnail() (data []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if ifd.thumbnailData == nil {
- log.Panic(ErrNoThumbnail)
- }
-
- return ifd.thumbnailData, nil
-}
-
-func (ifd *Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
- if tags == nil {
- tags = make([]*IfdTagEntry, 0)
- }
-
- // Now, print the tags while also descending to child-IFDS as we encounter them.
-
- ifdsFoundCount := 0
-
- for _, tag := range ifd.Entries {
- tags = append(tags, tag)
-
- if tag.ChildIfdPath != "" {
- ifdsFoundCount++
-
- childIfd, found := ifd.ChildIfdIndex[tag.ChildIfdPath]
- if found != true {
- log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdPath)
- }
-
- tags = childIfd.dumpTags(tags)
- }
- }
-
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
- }
-
- if ifd.NextIfd != nil {
- tags = ifd.NextIfd.dumpTags(tags)
- }
-
- return tags
-}
-
-// DumpTags prints the IFD hierarchy.
-func (ifd *Ifd) DumpTags() []*IfdTagEntry {
- return ifd.dumpTags(nil)
-}
-
-func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink bool) {
- indent := strings.Repeat(" ", level*2)
-
- prefix := " "
- if nextLink {
- prefix = ">"
- }
-
- fmt.Printf("%s%sIFD: %s\n", indent, prefix, ifd)
-
- // Now, print the tags while also descending to child-IFDS as we encounter them.
-
- ifdsFoundCount := 0
-
- for _, tag := range ifd.Entries {
- if tag.ChildIfdPath != "" {
- fmt.Printf("%s - TAG: %s\n", indent, tag)
- } else {
- it, err := ifd.tagIndex.Get(ifd.IfdPath, tag.TagId)
-
- tagName := ""
- if err == nil {
- tagName = it.Name
- }
-
- var value interface{}
- if populateValues == true {
- var err error
-
- value, err = ifd.TagValue(tag)
- if err != nil {
- if err == ErrUnhandledUnknownTypedTag {
- value = UnparseableUnknownTagValuePlaceholder
- } else {
- log.Panic(err)
- }
- }
- }
-
- fmt.Printf("%s - TAG: %s NAME=[%s] VALUE=[%v]\n", indent, tag, tagName, value)
- }
-
- if tag.ChildIfdPath != "" {
- ifdsFoundCount++
-
- childIfd, found := ifd.ChildIfdIndex[tag.ChildIfdPath]
- if found != true {
- log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdPath)
- }
-
- childIfd.printTagTree(populateValues, 0, level+1, false)
- }
- }
-
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
- }
-
- if ifd.NextIfd != nil {
- ifd.NextIfd.printTagTree(populateValues, index+1, level, true)
- }
-}
-
-// PrintTagTree prints the IFD hierarchy.
-func (ifd *Ifd) PrintTagTree(populateValues bool) {
- ifd.printTagTree(populateValues, 0, 0, false)
-}
-
-func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
- indent := strings.Repeat(" ", level*2)
-
- prefix := " "
- if nextLink {
- prefix = ">"
- }
-
- fmt.Printf("%s%s%s\n", indent, prefix, ifd)
-
- // Now, print the tags while also descending to child-IFDS as we encounter them.
-
- ifdsFoundCount := 0
-
- for _, tag := range ifd.Entries {
- if tag.ChildIfdPath != "" {
- ifdsFoundCount++
-
- childIfd, found := ifd.ChildIfdIndex[tag.ChildIfdPath]
- if found != true {
- log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdPath)
- }
-
- childIfd.printIfdTree(level+1, false)
- }
- }
-
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
- }
-
- if ifd.NextIfd != nil {
- ifd.NextIfd.printIfdTree(level, true)
- }
-}
-
-// PrintIfdTree prints the IFD hierarchy.
-func (ifd *Ifd) PrintIfdTree() {
- ifd.printIfdTree(0, false)
-}
-
-func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string {
- if tagsDump == nil {
- tagsDump = make([]string, 0)
- }
-
- indent := strings.Repeat(" ", level*2)
-
- var ifdPhrase string
- if ifd.ParentIfd != nil {
- ifdPhrase = fmt.Sprintf("[%s]->[%s]:(%d)", ifd.ParentIfd.IfdPath, ifd.IfdPath, ifd.Index)
- } else {
- ifdPhrase = fmt.Sprintf("[ROOT]->[%s]:(%d)", ifd.IfdPath, ifd.Index)
- }
-
- startBlurb := fmt.Sprintf("%s> IFD %s TOP", indent, ifdPhrase)
- tagsDump = append(tagsDump, startBlurb)
-
- ifdsFoundCount := 0
- for _, tag := range ifd.Entries {
- tagsDump = append(tagsDump, fmt.Sprintf("%s - (0x%04x)", indent, tag.TagId))
-
- if tag.ChildIfdPath != "" {
- ifdsFoundCount++
-
- childIfd, found := ifd.ChildIfdIndex[tag.ChildIfdPath]
- if found != true {
- log.Panicf("alien child IFD referenced by a tag: [%s]", tag.ChildIfdPath)
- }
-
- tagsDump = childIfd.dumpTree(tagsDump, level+1)
- }
- }
-
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
- }
-
- finishBlurb := fmt.Sprintf("%s< IFD %s BOTTOM", indent, ifdPhrase)
- tagsDump = append(tagsDump, finishBlurb)
-
- if ifd.NextIfd != nil {
- siblingBlurb := fmt.Sprintf("%s* LINKING TO SIBLING IFD [%s]:(%d)", indent, ifd.NextIfd.IfdPath, ifd.NextIfd.Index)
- tagsDump = append(tagsDump, siblingBlurb)
-
- tagsDump = ifd.NextIfd.dumpTree(tagsDump, level)
- }
-
- return tagsDump
-}
-
-// DumpTree returns a list of strings describing the IFD hierarchy.
-func (ifd *Ifd) DumpTree() []string {
- return ifd.dumpTree(nil, 0)
-}
-
-// GpsInfo parses and consolidates the GPS info. This can only be called on the
-// GPS IFD.
-func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Also add functionality to update the GPS info.
-
- gi = new(GpsInfo)
-
- if ifd.IfdPath != IfdPathStandardGps {
- log.Panicf("GPS can only be read on GPS IFD: [%s] != [%s]", ifd.IfdPath, IfdPathStandardGps)
- }
-
- if tags, found := ifd.EntriesByTagId[TagVersionId]; found == false {
- // We've seen this. We'll just have to default to assuming we're in a
- // 2.2.0.0 format.
- ifdEnumerateLogger.Warningf(nil, "No GPS version tag (0x%04x) found.", TagVersionId)
- } else {
- hit := false
- for _, acceptedGpsVersion := range ValidGpsVersions {
- if bytes.Compare(tags[0].value, acceptedGpsVersion[:]) == 0 {
- hit = true
- break
- }
- }
-
- if hit != true {
- ifdEnumerateLogger.Warningf(nil, "GPS version not supported: %v", tags[0].value)
- log.Panic(ErrNoGpsTags)
- }
- }
-
- tags, found := ifd.EntriesByTagId[TagLatitudeId]
- if found == false {
- ifdEnumerateLogger.Warningf(nil, "latitude not found")
- log.Panic(ErrNoGpsTags)
- }
-
- latitudeValue, err := ifd.TagValue(tags[0])
- log.PanicIf(err)
-
- // Look for whether North or South.
- tags, found = ifd.EntriesByTagId[TagLatitudeRefId]
- if found == false {
- ifdEnumerateLogger.Warningf(nil, "latitude-ref not found")
- log.Panic(ErrNoGpsTags)
- }
-
- latitudeRefValue, err := ifd.TagValue(tags[0])
- log.PanicIf(err)
-
- tags, found = ifd.EntriesByTagId[TagLongitudeId]
- if found == false {
- ifdEnumerateLogger.Warningf(nil, "longitude not found")
- log.Panic(ErrNoGpsTags)
- }
-
- longitudeValue, err := ifd.TagValue(tags[0])
- log.PanicIf(err)
-
- // Look for whether West or East.
- tags, found = ifd.EntriesByTagId[TagLongitudeRefId]
- if found == false {
- ifdEnumerateLogger.Warningf(nil, "longitude-ref not found")
- log.Panic(ErrNoGpsTags)
- }
-
- longitudeRefValue, err := ifd.TagValue(tags[0])
- log.PanicIf(err)
-
- // Parse location.
-
- latitudeRaw := latitudeValue.([]Rational)
-
- gi.Latitude = GpsDegrees{
- Orientation: latitudeRefValue.(string)[0],
- Degrees: float64(latitudeRaw[0].Numerator) / float64(latitudeRaw[0].Denominator),
- Minutes: float64(latitudeRaw[1].Numerator) / float64(latitudeRaw[1].Denominator),
- Seconds: float64(latitudeRaw[2].Numerator) / float64(latitudeRaw[2].Denominator),
- }
-
- longitudeRaw := longitudeValue.([]Rational)
-
- gi.Longitude = GpsDegrees{
- Orientation: longitudeRefValue.(string)[0],
- Degrees: float64(longitudeRaw[0].Numerator) / float64(longitudeRaw[0].Denominator),
- Minutes: float64(longitudeRaw[1].Numerator) / float64(longitudeRaw[1].Denominator),
- Seconds: float64(longitudeRaw[2].Numerator) / float64(longitudeRaw[2].Denominator),
- }
-
- // Parse altitude.
-
- altitudeTags, foundAltitude := ifd.EntriesByTagId[TagAltitudeId]
- altitudeRefTags, foundAltitudeRef := ifd.EntriesByTagId[TagAltitudeRefId]
-
- if foundAltitude == true && foundAltitudeRef == true {
- altitudeValue, err := ifd.TagValue(altitudeTags[0])
- log.PanicIf(err)
-
- altitudeRefValue, err := ifd.TagValue(altitudeRefTags[0])
- log.PanicIf(err)
-
- altitudeRaw := altitudeValue.([]Rational)
- altitude := int(altitudeRaw[0].Numerator / altitudeRaw[0].Denominator)
- if altitudeRefValue.([]byte)[0] == 1 {
- altitude *= -1
- }
-
- gi.Altitude = altitude
- }
-
- // Parse time.
-
- timestampTags, foundTimestamp := ifd.EntriesByTagId[TagTimestampId]
- datestampTags, foundDatestamp := ifd.EntriesByTagId[TagDatestampId]
-
- if foundTimestamp == true && foundDatestamp == true {
- datestampValue, err := ifd.TagValue(datestampTags[0])
- log.PanicIf(err)
-
- dateParts := strings.Split(datestampValue.(string), ":")
-
- year, err1 := strconv.ParseUint(dateParts[0], 10, 16)
- month, err2 := strconv.ParseUint(dateParts[1], 10, 8)
- day, err3 := strconv.ParseUint(dateParts[2], 10, 8)
-
- if err1 == nil && err2 == nil && err3 == nil {
- timestampValue, err := ifd.TagValue(timestampTags[0])
- log.PanicIf(err)
-
- timestampRaw := timestampValue.([]Rational)
-
- hour := int(timestampRaw[0].Numerator / timestampRaw[0].Denominator)
- minute := int(timestampRaw[1].Numerator / timestampRaw[1].Denominator)
- second := int(timestampRaw[2].Numerator / timestampRaw[2].Denominator)
-
- gi.Timestamp = time.Date(int(year), time.Month(month), int(day), hour, minute, second, 0, time.UTC)
- }
- }
-
- return gi, nil
-}
-
-type ParsedTagVisitor func(*Ifd, *IfdTagEntry) error
-
-func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- for ptr := ifd; ptr != nil; ptr = ptr.NextIfd {
- for _, ite := range ifd.Entries {
- if ite.ChildIfdPath != "" {
- childIfd := ifd.ChildIfdIndex[ite.ChildIfdPath]
-
- err := childIfd.EnumerateTagsRecursively(visitor)
- log.PanicIf(err)
- } else {
- err := visitor(ifd, ite)
- log.PanicIf(err)
- }
- }
- }
-
- return nil
-}
-
-func (ifd *Ifd) GetValueContext(ite *IfdTagEntry) *ValueContext {
- return newValueContextFromTag(
- ite,
- ifd.addressableData,
- ifd.ByteOrder)
-}
-
-type QueuedIfd struct {
- Name string
- IfdPath string
- FqIfdPath string
-
- TagId uint16
-
- Index int
- Offset uint32
- Parent *Ifd
-
- // ParentTagIndex is our tag position in the parent IFD, if we had a parent
- // (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
- // instead of as a child).
- ParentTagIndex int
-}
-
-type IfdIndex struct {
- RootIfd *Ifd
- Ifds []*Ifd
- Tree map[int]*Ifd
- Lookup map[string][]*Ifd
-}
-
-// Scan enumerates the different EXIF blocks (called IFDs).
-func (ie *IfdEnumerate) Collect(rootIfdOffset uint32, resolveValues bool) (index IfdIndex, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- tree := make(map[int]*Ifd)
- ifds := make([]*Ifd, 0)
- lookup := make(map[string][]*Ifd)
-
- queue := []QueuedIfd{
- {
- Name: IfdStandard,
- IfdPath: IfdStandard,
- FqIfdPath: IfdStandard,
-
- TagId: 0xffff,
-
- Index: 0,
- Offset: rootIfdOffset,
- },
- }
-
- edges := make(map[uint32]*Ifd)
-
- for {
- if len(queue) == 0 {
- break
- }
-
- qi := queue[0]
-
- name := qi.Name
- ifdPath := qi.IfdPath
- fqIfdPath := qi.FqIfdPath
-
- index := qi.Index
- offset := qi.Offset
- parentIfd := qi.Parent
-
- queue = queue[1:]
-
- ifdEnumerateLogger.Debugf(nil, "Parsing IFD [%s] (%d) at offset (%04x).", ifdPath, index, offset)
- ite := ie.getTagEnumerator(offset)
-
- nextIfdOffset, entries, thumbnailData, err := ie.ParseIfd(fqIfdPath, index, ite, nil, false, resolveValues)
- log.PanicIf(err)
-
- id := len(ifds)
-
- entriesByTagId := make(map[uint16][]*IfdTagEntry)
- for _, tag := range entries {
- tags, found := entriesByTagId[tag.TagId]
- if found == false {
- tags = make([]*IfdTagEntry, 0)
- }
-
- entriesByTagId[tag.TagId] = append(tags, tag)
- }
-
- ifd := &Ifd{
- addressableData: ie.exifData[ExifAddressableAreaStart:],
-
- ByteOrder: ie.byteOrder,
-
- Name: name,
- IfdPath: ifdPath,
- FqIfdPath: fqIfdPath,
-
- TagId: qi.TagId,
-
- Id: id,
-
- ParentIfd: parentIfd,
- ParentTagIndex: qi.ParentTagIndex,
-
- Index: index,
- Offset: offset,
- Entries: entries,
- EntriesByTagId: entriesByTagId,
-
- // This is populated as each child is processed.
- Children: make([]*Ifd, 0),
-
- NextIfdOffset: nextIfdOffset,
- thumbnailData: thumbnailData,
-
- ifdMapping: ie.ifdMapping,
- tagIndex: ie.tagIndex,
- }
-
- // Add ourselves to a big list of IFDs.
- ifds = append(ifds, ifd)
-
- // Install ourselves into a by-id lookup table (keys are unique).
- tree[id] = ifd
-
- // Install into by-name buckets.
-
- if list_, found := lookup[ifdPath]; found == true {
- lookup[ifdPath] = append(list_, ifd)
- } else {
- list_ = make([]*Ifd, 1)
- list_[0] = ifd
-
- lookup[ifdPath] = list_
- }
-
- // Add a link from the previous IFD in the chain to us.
- if previousIfd, found := edges[offset]; found == true {
- previousIfd.NextIfd = ifd
- }
-
- // Attach as a child to our parent (where we appeared as a tag in
- // that IFD).
- if parentIfd != nil {
- parentIfd.Children = append(parentIfd.Children, ifd)
- }
-
- // Determine if any of our entries is a child IFD and queue it.
- for i, entry := range entries {
- if entry.ChildIfdPath == "" {
- continue
- }
-
- qi := QueuedIfd{
- Name: entry.ChildIfdName,
- IfdPath: entry.ChildIfdPath,
- FqIfdPath: entry.ChildFqIfdPath,
- TagId: entry.TagId,
-
- Index: 0,
- Offset: entry.ValueOffset,
- Parent: ifd,
- ParentTagIndex: i,
- }
-
- queue = append(queue, qi)
- }
-
- // If there's another IFD in the chain.
- if nextIfdOffset != 0 {
- // Allow the next link to know what the previous link was.
- edges[nextIfdOffset] = ifd
-
- siblingIndex := index + 1
-
- var fqIfdPath string
- if parentIfd != nil {
- fqIfdPath = fmt.Sprintf("%s/%s%d", parentIfd.FqIfdPath, name, siblingIndex)
- } else {
- fqIfdPath = fmt.Sprintf("%s%d", name, siblingIndex)
- }
-
- qi := QueuedIfd{
- Name: name,
- IfdPath: ifdPath,
- FqIfdPath: fqIfdPath,
- TagId: 0xffff,
- Index: siblingIndex,
- Offset: nextIfdOffset,
- }
-
- queue = append(queue, qi)
- }
- }
-
- index.RootIfd = tree[0]
- index.Ifds = ifds
- index.Tree = tree
- index.Lookup = lookup
-
- err = ie.setChildrenIndex(index.RootIfd)
- log.PanicIf(err)
-
- return index, nil
-}
-
-func (ie *IfdEnumerate) setChildrenIndex(ifd *Ifd) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- childIfdIndex := make(map[string]*Ifd)
- for _, childIfd := range ifd.Children {
- childIfdIndex[childIfd.IfdPath] = childIfd
- }
-
- ifd.ChildIfdIndex = childIfdIndex
-
- for _, childIfd := range ifd.Children {
- err := ie.setChildrenIndex(childIfd)
- log.PanicIf(err)
- }
-
- return nil
-}
-
-// ParseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for
-// testing.
-func ParseOneIfd(ifdMapping *IfdMapping, tagIndex *TagIndex, fqIfdPath, ifdPath string, byteOrder binary.ByteOrder, ifdBlock []byte, visitor RawTagVisitor, resolveValues bool) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ie := NewIfdEnumerate(ifdMapping, tagIndex, make([]byte, 0), byteOrder)
- ite := NewIfdTagEnumerator(ifdBlock, byteOrder, 0)
-
- nextIfdOffset, entries, _, err = ie.ParseIfd(fqIfdPath, 0, ite, visitor, true, resolveValues)
- log.PanicIf(err)
-
- return nextIfdOffset, entries, nil
-}
-
-// ParseOneTag is a hack to use an IE to parse a raw tag block.
-func ParseOneTag(ifdMapping *IfdMapping, tagIndex *TagIndex, fqIfdPath, ifdPath string, byteOrder binary.ByteOrder, tagBlock []byte, resolveValue bool) (tag *IfdTagEntry, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ie := NewIfdEnumerate(ifdMapping, tagIndex, make([]byte, 0), byteOrder)
- ite := NewIfdTagEnumerator(tagBlock, byteOrder, 0)
-
- tag, err = ie.parseTag(fqIfdPath, 0, ite, resolveValue)
- log.PanicIf(err)
-
- return tag, nil
-}
-
-func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Add test.
-
- lineage, err := rootIfd.ifdMapping.ResolvePath(ifdPath)
- log.PanicIf(err)
-
- // Confirm the first IFD is our root IFD type, and then prune it because
- // from then on we'll be searching down through our children.
-
- if len(lineage) == 0 {
- log.Panicf("IFD path must be non-empty.")
- } else if lineage[0].Name != IfdStandard {
- log.Panicf("First IFD path item must be [%s].", IfdStandard)
- }
-
- desiredRootIndex := lineage[0].Index
- lineage = lineage[1:]
-
- // TODO(dustin): !! This is a poorly conceived fix that just doubles the work we already have to do below, which then interacts badly with the indices not being properly represented in the IFD-phrase.
- // TODO(dustin): !! <-- However, we're not sure whether we shouldn't store a secondary IFD-path with the indices. Some IFDs may not necessarily restrict which IFD indices they can be a child of (only the IFD itself matters). Validation should be delegated to the caller.
- thisIfd := rootIfd
- for currentRootIndex := 0; currentRootIndex < desiredRootIndex; currentRootIndex++ {
- if thisIfd.NextIfd == nil {
- log.Panicf("Root-IFD index (%d) does not exist in the data.", currentRootIndex)
- }
-
- thisIfd = thisIfd.NextIfd
- }
-
- for i, itii := range lineage {
- var hit *Ifd
- for _, childIfd := range thisIfd.Children {
- if childIfd.TagId == itii.TagId {
- hit = childIfd
- break
- }
- }
-
- // If we didn't find the child, add it.
- if hit == nil {
- log.Panicf("IFD [%s] in [%s] not found: %s", itii.Name, ifdPath, thisIfd.Children)
- }
-
- thisIfd = hit
-
- // If we didn't find the sibling, add it.
- for i = 0; i < itii.Index; i++ {
- if thisIfd.NextIfd == nil {
- log.Panicf("IFD [%s] does not have (%d) occurrences/siblings\n", thisIfd.IfdPath, itii.Index)
- }
-
- thisIfd = thisIfd.NextIfd
- }
- }
-
- return thisIfd, nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/ifd_tag_entry.go b/vendor/github.com/dsoprea/go-exif/ifd_tag_entry.go
deleted file mode 100644
index 59e79ccf7..000000000
--- a/vendor/github.com/dsoprea/go-exif/ifd_tag_entry.go
+++ /dev/null
@@ -1,233 +0,0 @@
-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 TagTypePrimitive
- UnitCount uint32
- ValueOffset uint32
- RawValueOffset []byte
-
- // ChildIfdName is the right most atom in the IFD-path. We need this to
- // construct the fully-qualified IFD-path.
- ChildIfdName string
-
- // ChildIfdPath is the IFD-path of the child if this tag represents a child
- // IFD.
- ChildIfdPath string
-
- // ChildFqIfdPath is the IFD-path of the child if this tag represents a
- // child IFD. Includes indices.
- ChildFqIfdPath string
-
- // 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.
-
- // IfdPath is the IFD that this tag belongs to.
- IfdPath string
-
- // TODO(dustin): !! We now parse and read the value immediately. Update the rest of the logic to use this and get rid of all of the staggered and different resolution mechanisms.
- value []byte
- isUnhandledUnknown bool
-}
-
-func (ite *IfdTagEntry) String() string {
- return fmt.Sprintf("IfdTagEntry<TAG-IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.IfdPath, ite.TagId, TypeNames[ite.TagType], ite.UnitCount)
-}
-
-// TODO(dustin): TODO(dustin): Stop exporting IfdPath and TagId.
-//
-// func (ite *IfdTagEntry) IfdPath() string {
-// return ite.IfdPath
-// }
-
-// TODO(dustin): TODO(dustin): Stop exporting IfdPath and TagId.
-//
-// func (ite *IfdTagEntry) TagId() uint16 {
-// return ite.TagId
-// }
-
-// ValueString renders a string from whatever the value in this tag is.
-func (ite *IfdTagEntry) ValueString(addressableData []byte, byteOrder binary.ByteOrder) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- valueContext :=
- newValueContextFromTag(
- ite,
- addressableData,
- byteOrder)
-
- if ite.TagType == TypeUndefined {
- valueRaw, err := valueContext.Undefined()
- log.PanicIf(err)
-
- value = fmt.Sprintf("%v", valueRaw)
- } else {
- value, err = valueContext.Format()
- log.PanicIf(err)
- }
-
- return value, nil
-}
-
-// ValueBytes renders a specific list of bytes from the value in this tag.
-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))
- }
- }()
-
- // Return the exact bytes of the unknown-type value. Returning a string
- // (`ValueString`) is easy because we can just pass everything to
- // `Sprintf()`. Returning the raw, typed value (`Value`) is easy
- // (obviously). However, here, in order to produce the list of bytes, we
- // need to coerce whatever `Undefined()` returns.
- if ite.TagType == TypeUndefined {
- valueContext :=
- newValueContextFromTag(
- ite,
- addressableData,
- byteOrder)
-
- value, err := valueContext.Undefined()
- log.PanicIf(err)
-
- switch value.(type) {
- case []byte:
- return value.([]byte), nil
- case TagUnknownType_UnknownValue:
- b := []byte(value.(TagUnknownType_UnknownValue))
- return b, 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%04x) (2): [%s]", ite.TagId, reflect.TypeOf(value))
- }
- }
-
- originalType := NewTagType(ite.TagType, byteOrder)
- byteCount := uint32(originalType.Type().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
-}
-
-// Value returns the specific, parsed, typed value from the tag.
-func (ite *IfdTagEntry) Value(addressableData []byte, byteOrder binary.ByteOrder) (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- valueContext :=
- newValueContextFromTag(
- ite,
- addressableData,
- byteOrder)
-
- if ite.TagType == TypeUndefined {
- value, err = valueContext.Undefined()
- log.PanicIf(err)
- } else {
- tt := NewTagType(ite.TagType, byteOrder)
-
- value, err = tt.Resolve(valueContext)
- 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) {
- return &IfdTagEntryValueResolver{
- addressableData: exifData[ExifAddressableAreaStart:],
- byteOrder: byteOrder,
- }
-}
-
-// ValueBytes will resolve embedded or allocated data from the tag and return the raw bytes.
-func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // OBSOLETE(dustin): This is now redundant. Use `(*ValueContext).readRawEncoded()` instead of this method.
-
- valueContext := newValueContextFromTag(
- ite,
- itevr.addressableData,
- itevr.byteOrder)
-
- rawBytes, err := valueContext.readRawEncoded()
- log.PanicIf(err)
-
- return rawBytes, nil
-}
-
-func (itevr *IfdTagEntryValueResolver) Value(ite *IfdTagEntry) (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // OBSOLETE(dustin): This is now redundant. Use `(*ValueContext).Values()` instead of this method.
-
- valueContext := newValueContextFromTag(
- ite,
- itevr.addressableData,
- itevr.byteOrder)
-
- values, err := valueContext.Values()
- log.PanicIf(err)
-
- return values, nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/package.go b/vendor/github.com/dsoprea/go-exif/package.go
deleted file mode 100644
index 2eb6b3056..000000000
--- a/vendor/github.com/dsoprea/go-exif/package.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// exif parses raw EXIF information given a block of raw EXIF data.
-//
-// v1 of go-exif is now deprecated. Please use v2.
-package exif
diff --git a/vendor/github.com/dsoprea/go-exif/parser.go b/vendor/github.com/dsoprea/go-exif/parser.go
deleted file mode 100644
index 4702db2f8..000000000
--- a/vendor/github.com/dsoprea/go-exif/parser.go
+++ /dev/null
@@ -1,190 +0,0 @@
-package exif
-
-import (
- "bytes"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-type Parser struct {
-}
-
-func (p *Parser) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeByte.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- value = []uint8(data[:count])
-
- return value, nil
-}
-
-// ParseAscii returns a string and auto-strips the trailing NUL character.
-func (p *Parser) ParseAscii(data []byte, unitCount uint32) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeAscii.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- if len(data) == 0 || data[count-1] != 0 {
- s := string(data[:count])
- typeLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s)
-
- return s, nil
- } else {
- // Auto-strip the NUL from the end. It serves no purpose outside of
- // encoding semantics.
-
- return string(data[:count-1]), nil
- }
-}
-
-// ParseAsciiNoNul returns a string without any consideration for a trailing NUL
-// character.
-func (p *Parser) ParseAsciiNoNul(data []byte, unitCount uint32) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeAscii.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- return string(data[:count]), nil
-}
-
-func (p *Parser) ParseShorts(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []uint16, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeShort.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- value = make([]uint16, count)
- for i := 0; i < count; i++ {
- value[i] = byteOrder.Uint16(data[i*2:])
- }
-
- return value, nil
-}
-
-func (p *Parser) ParseLongs(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []uint32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeLong.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- value = make([]uint32, count)
- for i := 0; i < count; i++ {
- value[i] = byteOrder.Uint32(data[i*4:])
- }
-
- return value, nil
-}
-
-func (p *Parser) ParseRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []Rational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeRational.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- value = make([]Rational, count)
- for i := 0; i < count; i++ {
- value[i].Numerator = byteOrder.Uint32(data[i*8:])
- value[i].Denominator = byteOrder.Uint32(data[i*8+4:])
- }
-
- return value, nil
-}
-
-func (p *Parser) ParseSignedLongs(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []int32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeSignedLong.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- b := bytes.NewBuffer(data)
-
- value = make([]int32, count)
- for i := 0; i < count; i++ {
- err := binary.Read(b, byteOrder, &value[i])
- log.PanicIf(err)
- }
-
- return value, nil
-}
-
-func (p *Parser) ParseSignedRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []SignedRational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- count := int(unitCount)
-
- if len(data) < (TypeSignedRational.Size() * count) {
- log.Panic(ErrNotEnoughData)
- }
-
- b := bytes.NewBuffer(data)
-
- value = make([]SignedRational, count)
- for i := 0; i < count; i++ {
- err = binary.Read(b, byteOrder, &value[i].Numerator)
- log.PanicIf(err)
-
- err = binary.Read(b, byteOrder, &value[i].Denominator)
- log.PanicIf(err)
- }
-
- return value, nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/tag_type.go b/vendor/github.com/dsoprea/go-exif/tag_type.go
deleted file mode 100644
index e53b1c498..000000000
--- a/vendor/github.com/dsoprea/go-exif/tag_type.go
+++ /dev/null
@@ -1,397 +0,0 @@
-package exif
-
-// NOTE(dustin): Most of this file encapsulates deprecated functionality and awaits being dumped in a future release.
-
-import (
- "fmt"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-type TagType struct {
- tagType TagTypePrimitive
- name string
- byteOrder binary.ByteOrder
-}
-
-func NewTagType(tagType TagTypePrimitive, byteOrder binary.ByteOrder) TagType {
- name, found := TypeNames[tagType]
- if found == false {
- log.Panicf("tag-type not valid: 0x%04x", tagType)
- }
-
- return TagType{
- tagType: tagType,
- name: name,
- byteOrder: byteOrder,
- }
-}
-
-func (tt TagType) String() string {
- return fmt.Sprintf("TagType<NAME=[%s]>", tt.name)
-}
-
-func (tt TagType) Name() string {
- return tt.name
-}
-
-func (tt TagType) Type() TagTypePrimitive {
- return tt.tagType
-}
-
-func (tt TagType) ByteOrder() binary.ByteOrder {
- return tt.byteOrder
-}
-
-func (tt TagType) Size() int {
-
- // DEPRECATED(dustin): `(TagTypePrimitive).Size()` should be used, directly.
-
- return tt.Type().Size()
-}
-
-// valueIsEmbedded will return a boolean indicating whether the value should be
-// found directly within the IFD entry or an offset to somewhere else.
-func (tt TagType) valueIsEmbedded(unitCount uint32) bool {
- return (tt.tagType.Size() * int(unitCount)) <= 4
-}
-
-func (tt TagType) readRawEncoded(valueContext ValueContext) (rawBytes []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- unitSizeRaw := uint32(tt.tagType.Size())
-
- if tt.valueIsEmbedded(valueContext.UnitCount()) == true {
- byteLength := unitSizeRaw * valueContext.UnitCount()
- return valueContext.RawValueOffset()[:byteLength], nil
- } else {
- return valueContext.AddressableData()[valueContext.ValueOffset() : valueContext.ValueOffset()+valueContext.UnitCount()*unitSizeRaw], nil
- }
-}
-
-func (tt TagType) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseBytes()` should be used.
-
- value, err = parser.ParseBytes(data, unitCount)
- log.PanicIf(err)
-
- return value, nil
-}
-
-// ParseAscii returns a string and auto-strips the trailing NUL character.
-func (tt TagType) ParseAscii(data []byte, unitCount uint32) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseAscii()` should be used.
-
- value, err = parser.ParseAscii(data, unitCount)
- log.PanicIf(err)
-
- return value, nil
-}
-
-// ParseAsciiNoNul returns a string without any consideration for a trailing NUL
-// character.
-func (tt TagType) ParseAsciiNoNul(data []byte, unitCount uint32) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseAsciiNoNul()` should be used.
-
- value, err = parser.ParseAsciiNoNul(data, unitCount)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ParseShorts(data []byte, unitCount uint32) (value []uint16, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseShorts()` should be used.
-
- value, err = parser.ParseShorts(data, unitCount, tt.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ParseLongs(data []byte, unitCount uint32) (value []uint32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseLongs()` should be used.
-
- value, err = parser.ParseLongs(data, unitCount, tt.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ParseRationals(data []byte, unitCount uint32) (value []Rational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseRationals()` should be used.
-
- value, err = parser.ParseRationals(data, unitCount, tt.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ParseSignedLongs(data []byte, unitCount uint32) (value []int32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseSignedLongs()` should be used.
-
- value, err = parser.ParseSignedLongs(data, unitCount, tt.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ParseSignedRationals(data []byte, unitCount uint32) (value []SignedRational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(*Parser).ParseSignedRationals()` should be used.
-
- value, err = parser.ParseSignedRationals(data, unitCount, tt.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadByteValues(valueContext ValueContext) (value []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadBytes()` should be used.
-
- value, err = valueContext.ReadBytes()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadAscii()` should be used.
-
- value, err = valueContext.ReadAscii()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadAsciiNoNulValue(valueContext ValueContext) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadAsciiNoNul()` should be used.
-
- value, err = valueContext.ReadAsciiNoNul()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadShortValues(valueContext ValueContext) (value []uint16, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadShorts()` should be used.
-
- value, err = valueContext.ReadShorts()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadLongValues(valueContext ValueContext) (value []uint32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadLongs()` should be used.
-
- value, err = valueContext.ReadLongs()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadRationalValues(valueContext ValueContext) (value []Rational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadRationals()` should be used.
-
- value, err = valueContext.ReadRationals()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadSignedLongValues(valueContext ValueContext) (value []int32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadSignedLongs()` should be used.
-
- value, err = valueContext.ReadSignedLongs()
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (tt TagType) ReadSignedRationalValues(valueContext ValueContext) (value []SignedRational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).ReadSignedRationals()` should be used.
-
- value, err = valueContext.ReadSignedRationals()
- log.PanicIf(err)
-
- return value, nil
-}
-
-// ResolveAsString resolves the given value and returns a flat string.
-//
-// Where the type is not ASCII, `justFirst` indicates whether to just stringify
-// the first item in the slice (or return an empty string if the slice is
-// empty).
-//
-// Since this method lacks the information to process unknown-type tags (e.g.
-// byte-order, tag-ID, IFD type), it will return an error if attempted. See
-// `Undefined()`.
-func (tt TagType) ResolveAsString(valueContext ValueContext, justFirst bool) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if justFirst == true {
- value, err = valueContext.FormatFirst()
- log.PanicIf(err)
- } else {
- value, err = valueContext.Format()
- log.PanicIf(err)
- }
-
- return value, nil
-}
-
-// Resolve knows how to resolve the given value.
-//
-// Since this method lacks the information to process unknown-type tags (e.g.
-// byte-order, tag-ID, IFD type), it will return an error if attempted. See
-// `Undefined()`.
-func (tt TagType) Resolve(valueContext *ValueContext) (values interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `(ValueContext).Values()` should be used.
-
- values, err = valueContext.Values()
- log.PanicIf(err)
-
- return values, nil
-}
-
-// Encode knows how to encode the given value to a byte slice.
-func (tt TagType) Encode(value interface{}) (encoded []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ve := NewValueEncoder(tt.byteOrder)
-
- ed, err := ve.EncodeWithType(tt, value)
- log.PanicIf(err)
-
- return ed.Encoded, err
-}
-
-func (tt TagType) FromString(valueString string) (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // DEPRECATED(dustin): `EncodeStringToBytes()` should be used.
-
- value, err = EncodeStringToBytes(tt.tagType, valueString)
- log.PanicIf(err)
-
- return value, nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/tags.go b/vendor/github.com/dsoprea/go-exif/tags.go
deleted file mode 100644
index 7f7e51555..000000000
--- a/vendor/github.com/dsoprea/go-exif/tags.go
+++ /dev/null
@@ -1,229 +0,0 @@
-package exif
-
-import (
- "fmt"
-
- "github.com/dsoprea/go-logging"
- "gopkg.in/yaml.v2"
-)
-
-const (
- // IFD1
-
- ThumbnailOffsetTagId = 0x0201
- ThumbnailSizeTagId = 0x0202
-
- // Exif
-
- TagVersionId = 0x0000
-
- TagLatitudeId = 0x0002
- TagLatitudeRefId = 0x0001
- TagLongitudeId = 0x0004
- TagLongitudeRefId = 0x0003
-
- TagTimestampId = 0x0007
- TagDatestampId = 0x001d
-
- TagAltitudeId = 0x0006
- TagAltitudeRefId = 0x0005
-)
-
-var (
- // tagsWithoutAlignment is a tag-lookup for tags whose value size won't
- // necessarily be a multiple of its tag-type.
- tagsWithoutAlignment = map[uint16]struct{}{
- // The thumbnail offset is stored as a long, but its data is a binary
- // blob (not a slice of longs).
- ThumbnailOffsetTagId: {},
- }
-)
-
-var (
- tagsLogger = log.NewLogger("exif.tags")
-)
-
-// File structures.
-
-type encodedTag struct {
- // id is signed, here, because YAML doesn't have enough information to
- // support unsigned.
- Id int `yaml:"id"`
- Name string `yaml:"name"`
- TypeName string `yaml:"type_name"`
-}
-
-// Indexing structures.
-
-type IndexedTag struct {
- Id uint16
- Name string
- IfdPath string
- Type TagTypePrimitive
-}
-
-func (it *IndexedTag) String() string {
- return fmt.Sprintf("TAG<ID=(0x%04x) NAME=[%s] IFD=[%s]>", it.Id, it.Name, it.IfdPath)
-}
-
-func (it *IndexedTag) IsName(ifdPath, name string) bool {
- return it.Name == name && it.IfdPath == ifdPath
-}
-
-func (it *IndexedTag) Is(ifdPath string, id uint16) bool {
- return it.Id == id && it.IfdPath == ifdPath
-}
-
-type TagIndex struct {
- tagsByIfd map[string]map[uint16]*IndexedTag
- tagsByIfdR map[string]map[string]*IndexedTag
-}
-
-func NewTagIndex() *TagIndex {
- ti := new(TagIndex)
-
- ti.tagsByIfd = make(map[string]map[uint16]*IndexedTag)
- ti.tagsByIfdR = make(map[string]map[string]*IndexedTag)
-
- return ti
-}
-
-func (ti *TagIndex) Add(it *IndexedTag) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // Store by ID.
-
- family, found := ti.tagsByIfd[it.IfdPath]
- if found == false {
- family = make(map[uint16]*IndexedTag)
- ti.tagsByIfd[it.IfdPath] = family
- }
-
- if _, found := family[it.Id]; found == true {
- log.Panicf("tag-ID defined more than once for IFD [%s]: (%02x)", it.IfdPath, it.Id)
- }
-
- family[it.Id] = it
-
- // Store by name.
-
- familyR, found := ti.tagsByIfdR[it.IfdPath]
- if found == false {
- familyR = make(map[string]*IndexedTag)
- ti.tagsByIfdR[it.IfdPath] = familyR
- }
-
- if _, found := familyR[it.Name]; found == true {
- log.Panicf("tag-name defined more than once for IFD [%s]: (%s)", it.IfdPath, it.Name)
- }
-
- familyR[it.Name] = it
-
- return nil
-}
-
-// Get returns information about the non-IFD tag.
-func (ti *TagIndex) Get(ifdPath string, id uint16) (it *IndexedTag, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if len(ti.tagsByIfd) == 0 {
- err := LoadStandardTags(ti)
- log.PanicIf(err)
- }
-
- family, found := ti.tagsByIfd[ifdPath]
- if found == false {
- log.Panic(ErrTagNotFound)
- }
-
- it, found = family[id]
- if found == false {
- log.Panic(ErrTagNotFound)
- }
-
- return it, nil
-}
-
-// Get returns information about the non-IFD tag.
-func (ti *TagIndex) GetWithName(ifdPath string, name string) (it *IndexedTag, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if len(ti.tagsByIfdR) == 0 {
- err := LoadStandardTags(ti)
- log.PanicIf(err)
- }
-
- it, found := ti.tagsByIfdR[ifdPath][name]
- if found != true {
- log.Panic(ErrTagNotFound)
- }
-
- return it, nil
-}
-
-// LoadStandardTags registers the tags that all devices/applications should
-// support.
-func LoadStandardTags(ti *TagIndex) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // Read static data.
-
- encodedIfds := make(map[string][]encodedTag)
-
- err = yaml.Unmarshal([]byte(tagsYaml), encodedIfds)
- log.PanicIf(err)
-
- // Load structure.
-
- count := 0
- for ifdPath, tags := range encodedIfds {
- for _, tagInfo := range tags {
- tagId := uint16(tagInfo.Id)
- tagName := tagInfo.Name
- tagTypeName := tagInfo.TypeName
-
- // TODO(dustin): !! Non-standard types, but found in real data. Ignore for right now.
- if tagTypeName == "SSHORT" || tagTypeName == "FLOAT" || tagTypeName == "DOUBLE" {
- continue
- }
-
- tagTypeId, found := TypeNamesR[tagTypeName]
- if found == false {
- log.Panicf("type [%s] for [%s] not valid", tagTypeName, tagName)
- continue
- }
-
- it := &IndexedTag{
- IfdPath: ifdPath,
- Id: tagId,
- Name: tagName,
- Type: tagTypeId,
- }
-
- err = ti.Add(it)
- log.PanicIf(err)
-
- count++
- }
- }
-
- tagsLogger.Debugf(nil, "(%d) tags loaded.", count)
-
- return nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/tags_data.go b/vendor/github.com/dsoprea/go-exif/tags_data.go
deleted file mode 100644
index 64ec458d3..000000000
--- a/vendor/github.com/dsoprea/go-exif/tags_data.go
+++ /dev/null
@@ -1,951 +0,0 @@
-package exif
-
-var (
- // From assets/tags.yaml . Needs to be here so it's embedded in the binary.
- tagsYaml = `
-# Notes:
-#
-# This file was produced from http://www.exiv2.org/tags.html, using the included
-# tool, though that document appears to have some duplicates when all IDs are
-# supposed to be unique (EXIF information only has IDs, not IFDs; IFDs are
-# determined by our pre-existing knowledge of those tags).
-#
-# The webpage that we've produced this file from appears to indicate that
-# ImageWidth is represented by both 0x0100 and 0x0001 depending on whether the
-# encoding is RGB or YCbCr.
-IFD/Exif:
-- id: 0x829a
- name: ExposureTime
- type_name: RATIONAL
-- id: 0x829d
- name: FNumber
- type_name: RATIONAL
-- id: 0x8822
- name: ExposureProgram
- type_name: SHORT
-- id: 0x8824
- name: SpectralSensitivity
- type_name: ASCII
-- id: 0x8827
- name: ISOSpeedRatings
- type_name: SHORT
-- id: 0x8828
- name: OECF
- type_name: UNDEFINED
-- id: 0x8830
- name: SensitivityType
- type_name: SHORT
-- id: 0x8831
- name: StandardOutputSensitivity
- type_name: LONG
-- id: 0x8832
- name: RecommendedExposureIndex
- type_name: LONG
-- id: 0x8833
- name: ISOSpeed
- type_name: LONG
-- id: 0x8834
- name: ISOSpeedLatitudeyyy
- type_name: LONG
-- id: 0x8835
- name: ISOSpeedLatitudezzz
- type_name: LONG
-- id: 0x9000
- name: ExifVersion
- type_name: UNDEFINED
-- id: 0x9003
- name: DateTimeOriginal
- type_name: ASCII
-- id: 0x9004
- name: DateTimeDigitized
- type_name: ASCII
-- id: 0x9101
- name: ComponentsConfiguration
- type_name: UNDEFINED
-- id: 0x9102
- name: CompressedBitsPerPixel
- type_name: RATIONAL
-- id: 0x9201
- name: ShutterSpeedValue
- type_name: SRATIONAL
-- id: 0x9202
- name: ApertureValue
- type_name: RATIONAL
-- id: 0x9203
- name: BrightnessValue
- type_name: SRATIONAL
-- id: 0x9204
- name: ExposureBiasValue
- type_name: SRATIONAL
-- id: 0x9205
- name: MaxApertureValue
- type_name: RATIONAL
-- id: 0x9206
- name: SubjectDistance
- type_name: RATIONAL
-- id: 0x9207
- name: MeteringMode
- type_name: SHORT
-- id: 0x9208
- name: LightSource
- type_name: SHORT
-- id: 0x9209
- name: Flash
- type_name: SHORT
-- id: 0x920a
- name: FocalLength
- type_name: RATIONAL
-- id: 0x9214
- name: SubjectArea
- type_name: SHORT
-- id: 0x927c
- name: MakerNote
- type_name: UNDEFINED
-- id: 0x9286
- name: UserComment
- type_name: UNDEFINED
-- id: 0x9290
- name: SubSecTime
- type_name: ASCII
-- id: 0x9291
- name: SubSecTimeOriginal
- type_name: ASCII
-- id: 0x9292
- name: SubSecTimeDigitized
- type_name: ASCII
-- id: 0xa000
- name: FlashpixVersion
- type_name: UNDEFINED
-- id: 0xa001
- name: ColorSpace
- type_name: SHORT
-- id: 0xa002
- name: PixelXDimension
- type_name: LONG
-- id: 0xa003
- name: PixelYDimension
- type_name: LONG
-- id: 0xa004
- name: RelatedSoundFile
- type_name: ASCII
-- id: 0xa005
- name: InteroperabilityTag
- type_name: LONG
-- id: 0xa20b
- name: FlashEnergy
- type_name: RATIONAL
-- id: 0xa20c
- name: SpatialFrequencyResponse
- type_name: UNDEFINED
-- id: 0xa20e
- name: FocalPlaneXResolution
- type_name: RATIONAL
-- id: 0xa20f
- name: FocalPlaneYResolution
- type_name: RATIONAL
-- id: 0xa210
- name: FocalPlaneResolutionUnit
- type_name: SHORT
-- id: 0xa214
- name: SubjectLocation
- type_name: SHORT
-- id: 0xa215
- name: ExposureIndex
- type_name: RATIONAL
-- id: 0xa217
- name: SensingMethod
- type_name: SHORT
-- id: 0xa300
- name: FileSource
- type_name: UNDEFINED
-- id: 0xa301
- name: SceneType
- type_name: UNDEFINED
-- id: 0xa302
- name: CFAPattern
- type_name: UNDEFINED
-- id: 0xa401
- name: CustomRendered
- type_name: SHORT
-- id: 0xa402
- name: ExposureMode
- type_name: SHORT
-- id: 0xa403
- name: WhiteBalance
- type_name: SHORT
-- id: 0xa404
- name: DigitalZoomRatio
- type_name: RATIONAL
-- id: 0xa405
- name: FocalLengthIn35mmFilm
- type_name: SHORT
-- id: 0xa406
- name: SceneCaptureType
- type_name: SHORT
-- id: 0xa407
- name: GainControl
- type_name: SHORT
-- id: 0xa408
- name: Contrast
- type_name: SHORT
-- id: 0xa409
- name: Saturation
- type_name: SHORT
-- id: 0xa40a
- name: Sharpness
- type_name: SHORT
-- id: 0xa40b
- name: DeviceSettingDescription
- type_name: UNDEFINED
-- id: 0xa40c
- name: SubjectDistanceRange
- type_name: SHORT
-- id: 0xa420
- name: ImageUniqueID
- type_name: ASCII
-- id: 0xa430
- name: CameraOwnerName
- type_name: ASCII
-- id: 0xa431
- name: BodySerialNumber
- type_name: ASCII
-- id: 0xa432
- name: LensSpecification
- type_name: RATIONAL
-- id: 0xa433
- name: LensMake
- type_name: ASCII
-- id: 0xa434
- name: LensModel
- type_name: ASCII
-- id: 0xa435
- name: LensSerialNumber
- type_name: ASCII
-IFD/GPSInfo:
-- id: 0x0000
- name: GPSVersionID
- type_name: BYTE
-- id: 0x0001
- name: GPSLatitudeRef
- type_name: ASCII
-- id: 0x0002
- name: GPSLatitude
- type_name: RATIONAL
-- id: 0x0003
- name: GPSLongitudeRef
- type_name: ASCII
-- id: 0x0004
- name: GPSLongitude
- type_name: RATIONAL
-- id: 0x0005
- name: GPSAltitudeRef
- type_name: BYTE
-- id: 0x0006
- name: GPSAltitude
- type_name: RATIONAL
-- id: 0x0007
- name: GPSTimeStamp
- type_name: RATIONAL
-- id: 0x0008
- name: GPSSatellites
- type_name: ASCII
-- id: 0x0009
- name: GPSStatus
- type_name: ASCII
-- id: 0x000a
- name: GPSMeasureMode
- type_name: ASCII
-- id: 0x000b
- name: GPSDOP
- type_name: RATIONAL
-- id: 0x000c
- name: GPSSpeedRef
- type_name: ASCII
-- id: 0x000d
- name: GPSSpeed
- type_name: RATIONAL
-- id: 0x000e
- name: GPSTrackRef
- type_name: ASCII
-- id: 0x000f
- name: GPSTrack
- type_name: RATIONAL
-- id: 0x0010
- name: GPSImgDirectionRef
- type_name: ASCII
-- id: 0x0011
- name: GPSImgDirection
- type_name: RATIONAL
-- id: 0x0012
- name: GPSMapDatum
- type_name: ASCII
-- id: 0x0013
- name: GPSDestLatitudeRef
- type_name: ASCII
-- id: 0x0014
- name: GPSDestLatitude
- type_name: RATIONAL
-- id: 0x0015
- name: GPSDestLongitudeRef
- type_name: ASCII
-- id: 0x0016
- name: GPSDestLongitude
- type_name: RATIONAL
-- id: 0x0017
- name: GPSDestBearingRef
- type_name: ASCII
-- id: 0x0018
- name: GPSDestBearing
- type_name: RATIONAL
-- id: 0x0019
- name: GPSDestDistanceRef
- type_name: ASCII
-- id: 0x001a
- name: GPSDestDistance
- type_name: RATIONAL
-- id: 0x001b
- name: GPSProcessingMethod
- type_name: UNDEFINED
-- id: 0x001c
- name: GPSAreaInformation
- type_name: UNDEFINED
-- id: 0x001d
- name: GPSDateStamp
- type_name: ASCII
-- id: 0x001e
- name: GPSDifferential
- type_name: SHORT
-IFD:
-- id: 0x000b
- name: ProcessingSoftware
- type_name: ASCII
-- id: 0x00fe
- name: NewSubfileType
- type_name: LONG
-- id: 0x00ff
- name: SubfileType
- type_name: SHORT
-- id: 0x0100
- name: ImageWidth
- type_name: LONG
-- id: 0x0101
- name: ImageLength
- type_name: LONG
-- id: 0x0102
- name: BitsPerSample
- type_name: SHORT
-- id: 0x0103
- name: Compression
- type_name: SHORT
-- id: 0x0106
- name: PhotometricInterpretation
- type_name: SHORT
-- id: 0x0107
- name: Thresholding
- type_name: SHORT
-- id: 0x0108
- name: CellWidth
- type_name: SHORT
-- id: 0x0109
- name: CellLength
- type_name: SHORT
-- id: 0x010a
- name: FillOrder
- type_name: SHORT
-- id: 0x010d
- name: DocumentName
- type_name: ASCII
-- id: 0x010e
- name: ImageDescription
- type_name: ASCII
-- id: 0x010f
- name: Make
- type_name: ASCII
-- id: 0x0110
- name: Model
- type_name: ASCII
-- id: 0x0111
- name: StripOffsets
- type_name: LONG
-- id: 0x0112
- name: Orientation
- type_name: SHORT
-- id: 0x0115
- name: SamplesPerPixel
- type_name: SHORT
-- id: 0x0116
- name: RowsPerStrip
- type_name: LONG
-- id: 0x0117
- name: StripByteCounts
- type_name: LONG
-- id: 0x011a
- name: XResolution
- type_name: RATIONAL
-- id: 0x011b
- name: YResolution
- type_name: RATIONAL
-- id: 0x011c
- name: PlanarConfiguration
- type_name: SHORT
-- id: 0x0122
- name: GrayResponseUnit
- type_name: SHORT
-- id: 0x0123
- name: GrayResponseCurve
- type_name: SHORT
-- id: 0x0124
- name: T4Options
- type_name: LONG
-- id: 0x0125
- name: T6Options
- type_name: LONG
-- id: 0x0128
- name: ResolutionUnit
- type_name: SHORT
-- id: 0x0129
- name: PageNumber
- type_name: SHORT
-- id: 0x012d
- name: TransferFunction
- type_name: SHORT
-- id: 0x0131
- name: Software
- type_name: ASCII
-- id: 0x0132
- name: DateTime
- type_name: ASCII
-- id: 0x013b
- name: Artist
- type_name: ASCII
-- id: 0x013c
- name: HostComputer
- type_name: ASCII
-- id: 0x013d
- name: Predictor
- type_name: SHORT
-- id: 0x013e
- name: WhitePoint
- type_name: RATIONAL
-- id: 0x013f
- name: PrimaryChromaticities
- type_name: RATIONAL
-- id: 0x0140
- name: ColorMap
- type_name: SHORT
-- id: 0x0141
- name: HalftoneHints
- type_name: SHORT
-- id: 0x0142
- name: TileWidth
- type_name: SHORT
-- id: 0x0143
- name: TileLength
- type_name: SHORT
-- id: 0x0144
- name: TileOffsets
- type_name: SHORT
-- id: 0x0145
- name: TileByteCounts
- type_name: SHORT
-- id: 0x014a
- name: SubIFDs
- type_name: LONG
-- id: 0x014c
- name: InkSet
- type_name: SHORT
-- id: 0x014d
- name: InkNames
- type_name: ASCII
-- id: 0x014e
- name: NumberOfInks
- type_name: SHORT
-- id: 0x0150
- name: DotRange
- type_name: BYTE
-- id: 0x0151
- name: TargetPrinter
- type_name: ASCII
-- id: 0x0152
- name: ExtraSamples
- type_name: SHORT
-- id: 0x0153
- name: SampleFormat
- type_name: SHORT
-- id: 0x0154
- name: SMinSampleValue
- type_name: SHORT
-- id: 0x0155
- name: SMaxSampleValue
- type_name: SHORT
-- id: 0x0156
- name: TransferRange
- type_name: SHORT
-- id: 0x0157
- name: ClipPath
- type_name: BYTE
-- id: 0x0158
- name: XClipPathUnits
- type_name: SSHORT
-- id: 0x0159
- name: YClipPathUnits
- type_name: SSHORT
-- id: 0x015a
- name: Indexed
- type_name: SHORT
-- id: 0x015b
- name: JPEGTables
- type_name: UNDEFINED
-- id: 0x015f
- name: OPIProxy
- type_name: SHORT
-- id: 0x0200
- name: JPEGProc
- type_name: LONG
-- id: 0x0201
- name: JPEGInterchangeFormat
- type_name: LONG
-- id: 0x0202
- name: JPEGInterchangeFormatLength
- type_name: LONG
-- id: 0x0203
- name: JPEGRestartInterval
- type_name: SHORT
-- id: 0x0205
- name: JPEGLosslessPredictors
- type_name: SHORT
-- id: 0x0206
- name: JPEGPointTransforms
- type_name: SHORT
-- id: 0x0207
- name: JPEGQTables
- type_name: LONG
-- id: 0x0208
- name: JPEGDCTables
- type_name: LONG
-- id: 0x0209
- name: JPEGACTables
- type_name: LONG
-- id: 0x0211
- name: YCbCrCoefficients
- type_name: RATIONAL
-- id: 0x0212
- name: YCbCrSubSampling
- type_name: SHORT
-- id: 0x0213
- name: YCbCrPositioning
- type_name: SHORT
-- id: 0x0214
- name: ReferenceBlackWhite
- type_name: RATIONAL
-- id: 0x02bc
- name: XMLPacket
- type_name: BYTE
-- id: 0x4746
- name: Rating
- type_name: SHORT
-- id: 0x4749
- name: RatingPercent
- type_name: SHORT
-- id: 0x800d
- name: ImageID
- type_name: ASCII
-- id: 0x828d
- name: CFARepeatPatternDim
- type_name: SHORT
-- id: 0x828e
- name: CFAPattern
- type_name: BYTE
-- id: 0x828f
- name: BatteryLevel
- type_name: RATIONAL
-- id: 0x8298
- name: Copyright
- type_name: ASCII
-- id: 0x829a
- name: ExposureTime
- type_name: RATIONAL
-- id: 0x829d
- name: FNumber
- type_name: RATIONAL
-- id: 0x83bb
- name: IPTCNAA
- type_name: LONG
-- id: 0x8649
- name: ImageResources
- type_name: BYTE
-- id: 0x8769
- name: ExifTag
- type_name: LONG
-- id: 0x8773
- name: InterColorProfile
- type_name: UNDEFINED
-- id: 0x8822
- name: ExposureProgram
- type_name: SHORT
-- id: 0x8824
- name: SpectralSensitivity
- type_name: ASCII
-- id: 0x8825
- name: GPSTag
- type_name: LONG
-- id: 0x8827
- name: ISOSpeedRatings
- type_name: SHORT
-- id: 0x8828
- name: OECF
- type_name: UNDEFINED
-- id: 0x8829
- name: Interlace
- type_name: SHORT
-- id: 0x882a
- name: TimeZoneOffset
- type_name: SSHORT
-- id: 0x882b
- name: SelfTimerMode
- type_name: SHORT
-- id: 0x9003
- name: DateTimeOriginal
- type_name: ASCII
-- id: 0x9102
- name: CompressedBitsPerPixel
- type_name: RATIONAL
-- id: 0x9201
- name: ShutterSpeedValue
- type_name: SRATIONAL
-- id: 0x9202
- name: ApertureValue
- type_name: RATIONAL
-- id: 0x9203
- name: BrightnessValue
- type_name: SRATIONAL
-- id: 0x9204
- name: ExposureBiasValue
- type_name: SRATIONAL
-- id: 0x9205
- name: MaxApertureValue
- type_name: RATIONAL
-- id: 0x9206
- name: SubjectDistance
- type_name: SRATIONAL
-- id: 0x9207
- name: MeteringMode
- type_name: SHORT
-- id: 0x9208
- name: LightSource
- type_name: SHORT
-- id: 0x9209
- name: Flash
- type_name: SHORT
-- id: 0x920a
- name: FocalLength
- type_name: RATIONAL
-- id: 0x920b
- name: FlashEnergy
- type_name: RATIONAL
-- id: 0x920c
- name: SpatialFrequencyResponse
- type_name: UNDEFINED
-- id: 0x920d
- name: Noise
- type_name: UNDEFINED
-- id: 0x920e
- name: FocalPlaneXResolution
- type_name: RATIONAL
-- id: 0x920f
- name: FocalPlaneYResolution
- type_name: RATIONAL
-- id: 0x9210
- name: FocalPlaneResolutionUnit
- type_name: SHORT
-- id: 0x9211
- name: ImageNumber
- type_name: LONG
-- id: 0x9212
- name: SecurityClassification
- type_name: ASCII
-- id: 0x9213
- name: ImageHistory
- type_name: ASCII
-- id: 0x9214
- name: SubjectLocation
- type_name: SHORT
-- id: 0x9215
- name: ExposureIndex
- type_name: RATIONAL
-- id: 0x9216
- name: TIFFEPStandardID
- type_name: BYTE
-- id: 0x9217
- name: SensingMethod
- type_name: SHORT
-- id: 0x9c9b
- name: XPTitle
- type_name: BYTE
-- id: 0x9c9c
- name: XPComment
- type_name: BYTE
-- id: 0x9c9d
- name: XPAuthor
- type_name: BYTE
-- id: 0x9c9e
- name: XPKeywords
- type_name: BYTE
-- id: 0x9c9f
- name: XPSubject
- type_name: BYTE
-- id: 0xc4a5
- name: PrintImageMatching
- type_name: UNDEFINED
-- id: 0xc612
- name: DNGVersion
- type_name: BYTE
-- id: 0xc613
- name: DNGBackwardVersion
- type_name: BYTE
-- id: 0xc614
- name: UniqueCameraModel
- type_name: ASCII
-- id: 0xc615
- name: LocalizedCameraModel
- type_name: BYTE
-- id: 0xc616
- name: CFAPlaneColor
- type_name: BYTE
-- id: 0xc617
- name: CFALayout
- type_name: SHORT
-- id: 0xc618
- name: LinearizationTable
- type_name: SHORT
-- id: 0xc619
- name: BlackLevelRepeatDim
- type_name: SHORT
-- id: 0xc61a
- name: BlackLevel
- type_name: RATIONAL
-- id: 0xc61b
- name: BlackLevelDeltaH
- type_name: SRATIONAL
-- id: 0xc61c
- name: BlackLevelDeltaV
- type_name: SRATIONAL
-- id: 0xc61d
- name: WhiteLevel
- type_name: SHORT
-- id: 0xc61e
- name: DefaultScale
- type_name: RATIONAL
-- id: 0xc61f
- name: DefaultCropOrigin
- type_name: SHORT
-- id: 0xc620
- name: DefaultCropSize
- type_name: SHORT
-- id: 0xc621
- name: ColorMatrix1
- type_name: SRATIONAL
-- id: 0xc622
- name: ColorMatrix2
- type_name: SRATIONAL
-- id: 0xc623
- name: CameraCalibration1
- type_name: SRATIONAL
-- id: 0xc624
- name: CameraCalibration2
- type_name: SRATIONAL
-- id: 0xc625
- name: ReductionMatrix1
- type_name: SRATIONAL
-- id: 0xc626
- name: ReductionMatrix2
- type_name: SRATIONAL
-- id: 0xc627
- name: AnalogBalance
- type_name: RATIONAL
-- id: 0xc628
- name: AsShotNeutral
- type_name: SHORT
-- id: 0xc629
- name: AsShotWhiteXY
- type_name: RATIONAL
-- id: 0xc62a
- name: BaselineExposure
- type_name: SRATIONAL
-- id: 0xc62b
- name: BaselineNoise
- type_name: RATIONAL
-- id: 0xc62c
- name: BaselineSharpness
- type_name: RATIONAL
-- id: 0xc62d
- name: BayerGreenSplit
- type_name: LONG
-- id: 0xc62e
- name: LinearResponseLimit
- type_name: RATIONAL
-- id: 0xc62f
- name: CameraSerialNumber
- type_name: ASCII
-- id: 0xc630
- name: LensInfo
- type_name: RATIONAL
-- id: 0xc631
- name: ChromaBlurRadius
- type_name: RATIONAL
-- id: 0xc632
- name: AntiAliasStrength
- type_name: RATIONAL
-- id: 0xc633
- name: ShadowScale
- type_name: SRATIONAL
-- id: 0xc634
- name: DNGPrivateData
- type_name: BYTE
-- id: 0xc635
- name: MakerNoteSafety
- type_name: SHORT
-- id: 0xc65a
- name: CalibrationIlluminant1
- type_name: SHORT
-- id: 0xc65b
- name: CalibrationIlluminant2
- type_name: SHORT
-- id: 0xc65c
- name: BestQualityScale
- type_name: RATIONAL
-- id: 0xc65d
- name: RawDataUniqueID
- type_name: BYTE
-- id: 0xc68b
- name: OriginalRawFileName
- type_name: BYTE
-- id: 0xc68c
- name: OriginalRawFileData
- type_name: UNDEFINED
-- id: 0xc68d
- name: ActiveArea
- type_name: SHORT
-- id: 0xc68e
- name: MaskedAreas
- type_name: SHORT
-- id: 0xc68f
- name: AsShotICCProfile
- type_name: UNDEFINED
-- id: 0xc690
- name: AsShotPreProfileMatrix
- type_name: SRATIONAL
-- id: 0xc691
- name: CurrentICCProfile
- type_name: UNDEFINED
-- id: 0xc692
- name: CurrentPreProfileMatrix
- type_name: SRATIONAL
-- id: 0xc6bf
- name: ColorimetricReference
- type_name: SHORT
-- id: 0xc6f3
- name: CameraCalibrationSignature
- type_name: BYTE
-- id: 0xc6f4
- name: ProfileCalibrationSignature
- type_name: BYTE
-- id: 0xc6f6
- name: AsShotProfileName
- type_name: BYTE
-- id: 0xc6f7
- name: NoiseReductionApplied
- type_name: RATIONAL
-- id: 0xc6f8
- name: ProfileName
- type_name: BYTE
-- id: 0xc6f9
- name: ProfileHueSatMapDims
- type_name: LONG
-- id: 0xc6fa
- name: ProfileHueSatMapData1
- type_name: FLOAT
-- id: 0xc6fb
- name: ProfileHueSatMapData2
- type_name: FLOAT
-- id: 0xc6fc
- name: ProfileToneCurve
- type_name: FLOAT
-- id: 0xc6fd
- name: ProfileEmbedPolicy
- type_name: LONG
-- id: 0xc6fe
- name: ProfileCopyright
- type_name: BYTE
-- id: 0xc714
- name: ForwardMatrix1
- type_name: SRATIONAL
-- id: 0xc715
- name: ForwardMatrix2
- type_name: SRATIONAL
-- id: 0xc716
- name: PreviewApplicationName
- type_name: BYTE
-- id: 0xc717
- name: PreviewApplicationVersion
- type_name: BYTE
-- id: 0xc718
- name: PreviewSettingsName
- type_name: BYTE
-- id: 0xc719
- name: PreviewSettingsDigest
- type_name: BYTE
-- id: 0xc71a
- name: PreviewColorSpace
- type_name: LONG
-- id: 0xc71b
- name: PreviewDateTime
- type_name: ASCII
-- id: 0xc71c
- name: RawImageDigest
- type_name: UNDEFINED
-- id: 0xc71d
- name: OriginalRawFileDigest
- type_name: UNDEFINED
-- id: 0xc71e
- name: SubTileBlockSize
- type_name: LONG
-- id: 0xc71f
- name: RowInterleaveFactor
- type_name: LONG
-- id: 0xc725
- name: ProfileLookTableDims
- type_name: LONG
-- id: 0xc726
- name: ProfileLookTableData
- type_name: FLOAT
-- id: 0xc740
- name: OpcodeList1
- type_name: UNDEFINED
-- id: 0xc741
- name: OpcodeList2
- type_name: UNDEFINED
-- id: 0xc74e
- name: OpcodeList3
- type_name: UNDEFINED
-- id: 0xc761
- name: NoiseProfile
- type_name: DOUBLE
-IFD/Exif/Iop:
-- id: 0x0001
- name: InteroperabilityIndex
- type_name: ASCII
-- id: 0x0002
- name: InteroperabilityVersion
- type_name: UNDEFINED
-- id: 0x1000
- name: RelatedImageFileFormat
- type_name: ASCII
-- id: 0x1001
- name: RelatedImageWidth
- type_name: LONG
-- id: 0x1002
- name: RelatedImageLength
- type_name: LONG
-`
-)
diff --git a/vendor/github.com/dsoprea/go-exif/tags_undefined.go b/vendor/github.com/dsoprea/go-exif/tags_undefined.go
deleted file mode 100644
index 63ba59323..000000000
--- a/vendor/github.com/dsoprea/go-exif/tags_undefined.go
+++ /dev/null
@@ -1,417 +0,0 @@
-package exif
-
-import (
- "bytes"
- "fmt"
- "strings"
-
- "crypto/sha1"
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-const (
- UnparseableUnknownTagValuePlaceholder = "!UNKNOWN"
-)
-
-// TODO(dustin): Rename "unknown" in symbol names to "undefined" in the next release.
-//
-// See https://github.com/dsoprea/go-exif/issues/27 .
-
-const (
- TagUnknownType_9298_UserComment_Encoding_ASCII = iota
- TagUnknownType_9298_UserComment_Encoding_JIS = iota
- TagUnknownType_9298_UserComment_Encoding_UNICODE = iota
- TagUnknownType_9298_UserComment_Encoding_UNDEFINED = iota
-)
-
-const (
- TagUnknownType_9101_ComponentsConfiguration_Channel_Y = 0x1
- TagUnknownType_9101_ComponentsConfiguration_Channel_Cb = 0x2
- TagUnknownType_9101_ComponentsConfiguration_Channel_Cr = 0x3
- TagUnknownType_9101_ComponentsConfiguration_Channel_R = 0x4
- TagUnknownType_9101_ComponentsConfiguration_Channel_G = 0x5
- TagUnknownType_9101_ComponentsConfiguration_Channel_B = 0x6
-)
-
-const (
- TagUnknownType_9101_ComponentsConfiguration_OTHER = iota
- TagUnknownType_9101_ComponentsConfiguration_RGB = iota
- TagUnknownType_9101_ComponentsConfiguration_YCBCR = iota
-)
-
-var (
- TagUnknownType_9298_UserComment_Encoding_Names = map[int]string{
- TagUnknownType_9298_UserComment_Encoding_ASCII: "ASCII",
- TagUnknownType_9298_UserComment_Encoding_JIS: "JIS",
- TagUnknownType_9298_UserComment_Encoding_UNICODE: "UNICODE",
- TagUnknownType_9298_UserComment_Encoding_UNDEFINED: "UNDEFINED",
- }
-
- TagUnknownType_9298_UserComment_Encodings = map[int][]byte{
- TagUnknownType_9298_UserComment_Encoding_ASCII: {'A', 'S', 'C', 'I', 'I', 0, 0, 0},
- TagUnknownType_9298_UserComment_Encoding_JIS: {'J', 'I', 'S', 0, 0, 0, 0, 0},
- TagUnknownType_9298_UserComment_Encoding_UNICODE: {'U', 'n', 'i', 'c', 'o', 'd', 'e', 0},
- TagUnknownType_9298_UserComment_Encoding_UNDEFINED: {0, 0, 0, 0, 0, 0, 0, 0},
- }
-
- TagUnknownType_9101_ComponentsConfiguration_Names = map[int]string{
- TagUnknownType_9101_ComponentsConfiguration_OTHER: "OTHER",
- TagUnknownType_9101_ComponentsConfiguration_RGB: "RGB",
- TagUnknownType_9101_ComponentsConfiguration_YCBCR: "YCBCR",
- }
-
- TagUnknownType_9101_ComponentsConfiguration_Configurations = map[int][]byte{
- TagUnknownType_9101_ComponentsConfiguration_RGB: {
- TagUnknownType_9101_ComponentsConfiguration_Channel_R,
- TagUnknownType_9101_ComponentsConfiguration_Channel_G,
- TagUnknownType_9101_ComponentsConfiguration_Channel_B,
- 0,
- },
-
- TagUnknownType_9101_ComponentsConfiguration_YCBCR: {
- TagUnknownType_9101_ComponentsConfiguration_Channel_Y,
- TagUnknownType_9101_ComponentsConfiguration_Channel_Cb,
- TagUnknownType_9101_ComponentsConfiguration_Channel_Cr,
- 0,
- },
- }
-)
-
-// TODO(dustin): Rename `UnknownTagValue` to `UndefinedTagValue`.
-
-type UnknownTagValue interface {
- ValueBytes() ([]byte, error)
-}
-
-// TODO(dustin): Rename `TagUnknownType_GeneralString` to `TagUnknownType_GeneralString`.
-
-type TagUnknownType_GeneralString string
-
-func (gs TagUnknownType_GeneralString) ValueBytes() (value []byte, err error) {
- return []byte(gs), nil
-}
-
-// TODO(dustin): Rename `TagUnknownType_9298_UserComment` to `TagUndefinedType_9298_UserComment`.
-
-type TagUnknownType_9298_UserComment struct {
- EncodingType int
- EncodingBytes []byte
-}
-
-func (uc TagUnknownType_9298_UserComment) String() string {
- var valuePhrase string
-
- if len(uc.EncodingBytes) <= 8 {
- valuePhrase = fmt.Sprintf("%v", uc.EncodingBytes)
- } else {
- valuePhrase = fmt.Sprintf("%v...", uc.EncodingBytes[:8])
- }
-
- return fmt.Sprintf("UserComment<SIZE=(%d) ENCODING=[%s] V=%v LEN=(%d)>", len(uc.EncodingBytes), TagUnknownType_9298_UserComment_Encoding_Names[uc.EncodingType], valuePhrase, len(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)
- copy(value[8:], uc.EncodingBytes)
-
- return value, nil
-}
-
-// TODO(dustin): Rename `TagUnknownType_927C_MakerNote` to `TagUndefinedType_927C_MakerNote`.
-
-type TagUnknownType_927C_MakerNote struct {
- MakerNoteType []byte
- MakerNoteBytes []byte
-}
-
-func (mn TagUnknownType_927C_MakerNote) String() string {
- parts := make([]string, 20)
- for i, c := range mn.MakerNoteType {
- parts[i] = fmt.Sprintf("%02x", c)
- }
-
- h := sha1.New()
-
- _, err := h.Write(mn.MakerNoteBytes)
- log.PanicIf(err)
-
- digest := h.Sum(nil)
-
- return fmt.Sprintf("MakerNote<TYPE-ID=[%s] LEN=(%d) SHA1=[%020x]>", strings.Join(parts, " "), len(mn.MakerNoteBytes), digest)
-}
-
-func (uc TagUnknownType_927C_MakerNote) ValueBytes() (value []byte, err error) {
- return uc.MakerNoteBytes, nil
-}
-
-// TODO(dustin): Rename `TagUnknownType_9101_ComponentsConfiguration` to `TagUndefinedType_9101_ComponentsConfiguration`.
-
-type TagUnknownType_9101_ComponentsConfiguration struct {
- ConfigurationId int
- ConfigurationBytes []byte
-}
-
-func (cc TagUnknownType_9101_ComponentsConfiguration) String() string {
- return fmt.Sprintf("ComponentsConfiguration<ID=[%s] BYTES=%v>", TagUnknownType_9101_ComponentsConfiguration_Names[cc.ConfigurationId], cc.ConfigurationBytes)
-}
-
-func (uc TagUnknownType_9101_ComponentsConfiguration) ValueBytes() (value []byte, err error) {
- return uc.ConfigurationBytes, nil
-}
-
-// TODO(dustin): Rename `EncodeUnknown_9286` to `EncodeUndefined_9286`.
-
-func EncodeUnknown_9286(uc TagUnknownType_9298_UserComment) (encoded []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- b := new(bytes.Buffer)
-
- encodingTypeBytes := TagUnknownType_9298_UserComment_Encodings[uc.EncodingType]
-
- _, err = b.Write(encodingTypeBytes)
- log.PanicIf(err)
-
- _, err = b.Write(uc.EncodingBytes)
- log.PanicIf(err)
-
- return b.Bytes(), nil
-}
-
-type EncodeableUndefinedValue struct {
- IfdPath string
- TagId uint16
- Parameters interface{}
-}
-
-func EncodeUndefined(ifdPath string, tagId uint16, value interface{}) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Finish implementing these.
- if ifdPath == IfdPathStandardExif {
- if tagId == 0x9286 {
- encoded, err := EncodeUnknown_9286(value.(TagUnknownType_9298_UserComment))
- log.PanicIf(err)
-
- ed.Type = TypeUndefined
- ed.Encoded = encoded
- ed.UnitCount = uint32(len(encoded))
-
- return ed, nil
- }
- }
-
- log.Panicf("undefined value not encodable: %s (0x%02x)", ifdPath, tagId)
-
- // Never called.
- return EncodedData{}, nil
-}
-
-// TODO(dustin): Rename `TagUnknownType_UnknownValue` to `TagUndefinedType_UnknownValue`.
-
-type TagUnknownType_UnknownValue []byte
-
-func (tutuv TagUnknownType_UnknownValue) String() string {
- parts := make([]string, len(tutuv))
- for i, c := range tutuv {
- parts[i] = fmt.Sprintf("%02x", c)
- }
-
- h := sha1.New()
-
- _, err := h.Write(tutuv)
- log.PanicIf(err)
-
- digest := h.Sum(nil)
-
- return fmt.Sprintf("Unknown<DATA=[%s] LEN=(%d) SHA1=[%020x]>", strings.Join(parts, " "), len(tutuv), digest)
-}
-
-// UndefinedValue knows how to resolve the value for most unknown-type tags.
-func UndefinedValue(ifdPath string, tagId uint16, valueContext interface{}, byteOrder binary.ByteOrder) (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): Stop exporting this. Use `(*ValueContext).Undefined()`.
-
- var valueContextPtr *ValueContext
-
- if vc, ok := valueContext.(*ValueContext); ok == true {
- // Legacy usage.
-
- valueContextPtr = vc
- } else {
- // Standard usage.
-
- valueContextValue := valueContext.(ValueContext)
- valueContextPtr = &valueContextValue
- }
-
- typeLogger.Debugf(nil, "UndefinedValue: IFD-PATH=[%s] TAG-ID=(0x%02x)", ifdPath, tagId)
-
- if ifdPath == IfdPathStandardExif {
- if tagId == 0x9000 {
- // ExifVersion
-
- valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
-
- valueString, err := valueContextPtr.ReadAsciiNoNul()
- log.PanicIf(err)
-
- return TagUnknownType_GeneralString(valueString), nil
- } else if tagId == 0xa000 {
- // FlashpixVersion
-
- valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
-
- valueString, err := valueContextPtr.ReadAsciiNoNul()
- log.PanicIf(err)
-
- return TagUnknownType_GeneralString(valueString), nil
- } else if tagId == 0x9286 {
- // UserComment
-
- valueContextPtr.SetUnknownValueType(TypeByte)
-
- valueBytes, err := valueContextPtr.ReadBytes()
- log.PanicIf(err)
-
- unknownUc := TagUnknownType_9298_UserComment{
- EncodingType: TagUnknownType_9298_UserComment_Encoding_UNDEFINED,
- EncodingBytes: []byte{},
- }
-
- encoding := valueBytes[:8]
- for encodingIndex, encodingBytes := range TagUnknownType_9298_UserComment_Encodings {
- if bytes.Compare(encoding, encodingBytes) == 0 {
- uc := TagUnknownType_9298_UserComment{
- EncodingType: encodingIndex,
- EncodingBytes: valueBytes[8:],
- }
-
- return uc, nil
- }
- }
-
- typeLogger.Warningf(nil, "User-comment encoding not valid. Returning 'unknown' type (the default).")
- return unknownUc, nil
- } else if tagId == 0x927c {
- // MakerNote
- // TODO(dustin): !! This is the Wild Wild West. This very well might be a child IFD, but any and all OEM's define their own formats. If we're going to be writing changes and this is complete EXIF (which may not have the first eight bytes), it might be fine. However, if these are just IFDs they'll be relative to the main EXIF, this will invalidate the MakerNote data for IFDs and any other implementations that use offsets unless we can interpret them all. It be best to return to this later and just exclude this from being written for now, though means a loss of a wealth of image metadata.
- // -> We can also just blindly try to interpret as an IFD and just validate that it's looks good (maybe it will even have a 'next ifd' pointer that we can validate is 0x0).
-
- valueContextPtr.SetUnknownValueType(TypeByte)
-
- valueBytes, err := valueContextPtr.ReadBytes()
- log.PanicIf(err)
-
- // TODO(dustin): Doesn't work, but here as an example.
- // ie := NewIfdEnumerate(valueBytes, byteOrder)
-
- // // TODO(dustin): !! Validate types (might have proprietary types, but it might be worth splitting the list between valid and not valid; maybe fail if a certain proportion are invalid, or maybe aren't less then a certain small integer)?
- // ii, err := ie.Collect(0x0)
-
- // for _, entry := range ii.RootIfd.Entries {
- // fmt.Printf("ENTRY: 0x%02x %d\n", entry.TagId, entry.TagType)
- // }
-
- mn := TagUnknownType_927C_MakerNote{
- MakerNoteType: valueBytes[:20],
-
- // MakerNoteBytes has the whole length of bytes. There's always
- // the chance that the first 20 bytes includes actual data.
- MakerNoteBytes: valueBytes,
- }
-
- return mn, nil
- } else if tagId == 0x9101 {
- // ComponentsConfiguration
-
- valueContextPtr.SetUnknownValueType(TypeByte)
-
- valueBytes, err := valueContextPtr.ReadBytes()
- log.PanicIf(err)
-
- for configurationId, configurationBytes := range TagUnknownType_9101_ComponentsConfiguration_Configurations {
- if bytes.Compare(valueBytes, configurationBytes) == 0 {
- cc := TagUnknownType_9101_ComponentsConfiguration{
- ConfigurationId: configurationId,
- ConfigurationBytes: valueBytes,
- }
-
- return cc, nil
- }
- }
-
- cc := TagUnknownType_9101_ComponentsConfiguration{
- ConfigurationId: TagUnknownType_9101_ComponentsConfiguration_OTHER,
- ConfigurationBytes: valueBytes,
- }
-
- return cc, nil
- }
- } else if ifdPath == IfdPathStandardGps {
- if tagId == 0x001c {
- // GPSAreaInformation
-
- valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
-
- valueString, err := valueContextPtr.ReadAsciiNoNul()
- log.PanicIf(err)
-
- return TagUnknownType_GeneralString(valueString), nil
- } else if tagId == 0x001b {
- // GPSProcessingMethod
-
- valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
-
- valueString, err := valueContextPtr.ReadAsciiNoNul()
- log.PanicIf(err)
-
- return TagUnknownType_GeneralString(valueString), nil
- }
- } else if ifdPath == IfdPathStandardExifIop {
- if tagId == 0x0002 {
- // InteropVersion
-
- valueContextPtr.SetUnknownValueType(TypeAsciiNoNul)
-
- valueString, err := valueContextPtr.ReadAsciiNoNul()
- log.PanicIf(err)
-
- return TagUnknownType_GeneralString(valueString), nil
- }
- }
-
- // TODO(dustin): !! Still need to do:
- //
- // complex: 0xa302, 0xa20c, 0x8828
- // long: 0xa301, 0xa300
- //
- // 0xa40b is device-specific and unhandled.
- //
- // See https://github.com/dsoprea/go-exif/issues/26.
-
- // We have no choice but to return the error. We have no way of knowing how
- // much data there is without already knowing what data-type this tag is.
- return nil, ErrUnhandledUnknownTypedTag
-}
diff --git a/vendor/github.com/dsoprea/go-exif/type.go b/vendor/github.com/dsoprea/go-exif/type.go
deleted file mode 100644
index 2012b6067..000000000
--- a/vendor/github.com/dsoprea/go-exif/type.go
+++ /dev/null
@@ -1,310 +0,0 @@
-package exif
-
-import (
- "errors"
- "fmt"
- "strconv"
- "strings"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-type TagTypePrimitive uint16
-
-func (typeType TagTypePrimitive) String() string {
- return TypeNames[typeType]
-}
-
-func (tagType TagTypePrimitive) Size() int {
- if tagType == TypeByte {
- return 1
- } else if tagType == TypeAscii || tagType == TypeAsciiNoNul {
- return 1
- } else if tagType == TypeShort {
- return 2
- } else if tagType == TypeLong {
- return 4
- } else if tagType == TypeRational {
- return 8
- } else if tagType == TypeSignedLong {
- return 4
- } else if tagType == TypeSignedRational {
- return 8
- } else {
- log.Panicf("can not determine tag-value size for type (%d): [%s]", tagType, TypeNames[tagType])
-
- // Never called.
- return 0
- }
-}
-
-const (
- TypeByte TagTypePrimitive = 1
- TypeAscii TagTypePrimitive = 2
- TypeShort TagTypePrimitive = 3
- TypeLong TagTypePrimitive = 4
- TypeRational TagTypePrimitive = 5
- TypeUndefined TagTypePrimitive = 7
- TypeSignedLong TagTypePrimitive = 9
- TypeSignedRational TagTypePrimitive = 10
-
- // TypeAsciiNoNul is just a pseudo-type, for our own purposes.
- TypeAsciiNoNul TagTypePrimitive = 0xf0
-)
-
-var (
- typeLogger = log.NewLogger("exif.type")
-)
-
-var (
- // TODO(dustin): Rename TypeNames() to typeNames() and add getter.
- TypeNames = map[TagTypePrimitive]string{
- TypeByte: "BYTE",
- TypeAscii: "ASCII",
- TypeShort: "SHORT",
- TypeLong: "LONG",
- TypeRational: "RATIONAL",
- TypeUndefined: "UNDEFINED",
- TypeSignedLong: "SLONG",
- TypeSignedRational: "SRATIONAL",
-
- TypeAsciiNoNul: "_ASCII_NO_NUL",
- }
-
- TypeNamesR = map[string]TagTypePrimitive{}
-)
-
-var (
- // 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")
-
- // ErrWrongType is used when we try to parse anything other than the
- // current type.
- ErrWrongType = errors.New("wrong type, can not parse")
-
- // ErrUnhandledUnknownTag is used when we try to parse a tag that's
- // recorded as an "unknown" type but not a documented tag (therefore
- // leaving us not knowning how to read it).
- ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag")
-)
-
-type Rational struct {
- Numerator uint32
- Denominator uint32
-}
-
-type SignedRational struct {
- Numerator int32
- Denominator int32
-}
-
-func TagTypeSize(tagType TagTypePrimitive) int {
-
- // DEPRECATED(dustin): `(TagTypePrimitive).Size()` should be used, directly.
-
- return tagType.Size()
-}
-
-// Format returns a stringified value for the given bytes. Automatically
-// calculates count based on type size.
-func Format(rawBytes []byte, tagType TagTypePrimitive, justFirst bool, byteOrder binary.ByteOrder) (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): !! Add tests
-
- typeSize := tagType.Size()
-
- if len(rawBytes)%typeSize != 0 {
- log.Panicf("byte-count (%d) does not align for [%s] type with a size of (%d) bytes", len(rawBytes), TypeNames[tagType], typeSize)
- }
-
- // unitCount is the calculated unit-count. This should equal the original
- // value from the tag (pre-resolution).
- unitCount := uint32(len(rawBytes) / typeSize)
-
- // Truncate the items if it's not bytes or a string and we just want the first.
-
- valueSuffix := ""
- if justFirst == true && unitCount > 1 && tagType != TypeByte && tagType != TypeAscii && tagType != TypeAsciiNoNul {
- unitCount = 1
- valueSuffix = "..."
- }
-
- if tagType == TypeByte {
- items, err := parser.ParseBytes(rawBytes, unitCount)
- log.PanicIf(err)
-
- return DumpBytesToString(items), nil
- } else if tagType == TypeAscii {
- phrase, err := parser.ParseAscii(rawBytes, unitCount)
- log.PanicIf(err)
-
- return phrase, nil
- } else if tagType == TypeAsciiNoNul {
- phrase, err := parser.ParseAsciiNoNul(rawBytes, unitCount)
- log.PanicIf(err)
-
- return phrase, nil
- } else if tagType == TypeShort {
- items, err := parser.ParseShorts(rawBytes, unitCount, byteOrder)
- log.PanicIf(err)
-
- if len(items) > 0 {
- if justFirst == true {
- return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
- } else {
- return fmt.Sprintf("%v", items), nil
- }
- } else {
- return "", nil
- }
- } else if tagType == TypeLong {
- items, err := parser.ParseLongs(rawBytes, unitCount, byteOrder)
- log.PanicIf(err)
-
- if len(items) > 0 {
- if justFirst == true {
- return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
- } else {
- return fmt.Sprintf("%v", items), nil
- }
- } else {
- return "", nil
- }
- } else if tagType == TypeRational {
- items, err := parser.ParseRationals(rawBytes, unitCount, byteOrder)
- log.PanicIf(err)
-
- if len(items) > 0 {
- parts := make([]string, len(items))
- for i, r := range items {
- parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
- }
-
- if justFirst == true {
- return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
- } else {
- return fmt.Sprintf("%v", parts), nil
- }
- } else {
- return "", nil
- }
- } else if tagType == TypeSignedLong {
- items, err := parser.ParseSignedLongs(rawBytes, unitCount, byteOrder)
- log.PanicIf(err)
-
- if len(items) > 0 {
- if justFirst == true {
- return fmt.Sprintf("%v%s", items[0], valueSuffix), nil
- } else {
- return fmt.Sprintf("%v", items), nil
- }
- } else {
- return "", nil
- }
- } else if tagType == TypeSignedRational {
- items, err := parser.ParseSignedRationals(rawBytes, unitCount, byteOrder)
- log.PanicIf(err)
-
- parts := make([]string, len(items))
- for i, r := range items {
- parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
- }
-
- if len(items) > 0 {
- if justFirst == true {
- return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
- } else {
- return fmt.Sprintf("%v", parts), nil
- }
- } else {
- return "", nil
- }
- } else {
- // Affects only "unknown" values, in general.
- log.Panicf("value of type [%s] can not be formatted into string", tagType.String())
-
- // Never called.
- return "", nil
- }
-}
-
-func EncodeStringToBytes(tagType TagTypePrimitive, valueString string) (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if tagType == TypeUndefined {
- // TODO(dustin): Circle back to this.
- log.Panicf("undefined-type values are not supported")
- }
-
- if tagType == TypeByte {
- return []byte(valueString), nil
- } else if tagType == TypeAscii || tagType == TypeAsciiNoNul {
- // Whether or not we're putting an NUL on the end is only relevant for
- // byte-level encoding. This function really just supports a user
- // interface.
-
- return valueString, nil
- } else if tagType == TypeShort {
- n, err := strconv.ParseUint(valueString, 10, 16)
- log.PanicIf(err)
-
- return uint16(n), nil
- } else if tagType == TypeLong {
- n, err := strconv.ParseUint(valueString, 10, 32)
- log.PanicIf(err)
-
- return uint32(n), nil
- } else if tagType == TypeRational {
- parts := strings.SplitN(valueString, "/", 2)
-
- numerator, err := strconv.ParseUint(parts[0], 10, 32)
- log.PanicIf(err)
-
- denominator, err := strconv.ParseUint(parts[1], 10, 32)
- log.PanicIf(err)
-
- return Rational{
- Numerator: uint32(numerator),
- Denominator: uint32(denominator),
- }, nil
- } else if tagType == TypeSignedLong {
- n, err := strconv.ParseInt(valueString, 10, 32)
- log.PanicIf(err)
-
- return int32(n), nil
- } else if tagType == TypeSignedRational {
- parts := strings.SplitN(valueString, "/", 2)
-
- numerator, err := strconv.ParseInt(parts[0], 10, 32)
- log.PanicIf(err)
-
- denominator, err := strconv.ParseInt(parts[1], 10, 32)
- log.PanicIf(err)
-
- return SignedRational{
- Numerator: int32(numerator),
- Denominator: int32(denominator),
- }, nil
- }
-
- log.Panicf("from-string encoding for type not supported; this shouldn't happen: [%s]", tagType.String())
- return nil, nil
-}
-
-func init() {
- for typeId, typeName := range TypeNames {
- TypeNamesR[typeName] = typeId
- }
-}
diff --git a/vendor/github.com/dsoprea/go-exif/type_encode.go b/vendor/github.com/dsoprea/go-exif/type_encode.go
deleted file mode 100644
index 9ff754916..000000000
--- a/vendor/github.com/dsoprea/go-exif/type_encode.go
+++ /dev/null
@@ -1,262 +0,0 @@
-package exif
-
-import (
- "bytes"
- "reflect"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-var (
- typeEncodeLogger = log.NewLogger("exif.type_encode")
-)
-
-// EncodedData encapsulates the compound output of an encoding operation.
-type EncodedData struct {
- Type TagTypePrimitive
- Encoded []byte
-
- // TODO(dustin): Is this really necessary? We might have this just to correlate to the incoming stream format (raw bytes and a unit-count both for incoming and outgoing).
- UnitCount uint32
-}
-
-type ValueEncoder struct {
- byteOrder binary.ByteOrder
-}
-
-func NewValueEncoder(byteOrder binary.ByteOrder) *ValueEncoder {
- return &ValueEncoder{
- byteOrder: byteOrder,
- }
-}
-
-func (ve *ValueEncoder) encodeBytes(value []uint8) (ed EncodedData, err error) {
- ed.Type = TypeByte
- ed.Encoded = []byte(value)
- ed.UnitCount = uint32(len(value))
-
- return ed, nil
-}
-
-func (ve *ValueEncoder) encodeAscii(value string) (ed EncodedData, err error) {
- ed.Type = TypeAscii
-
- ed.Encoded = []byte(value)
- ed.Encoded = append(ed.Encoded, 0)
-
- ed.UnitCount = uint32(len(ed.Encoded))
-
- return ed, nil
-}
-
-// encodeAsciiNoNul returns a string encoded as a byte-string without a trailing
-// NUL byte.
-//
-// Note that:
-//
-// 1. This type can not be automatically encoded using `Encode()`. The default
-// mode is to encode *with* a trailing NUL byte using `encodeAscii`. Only
-// certain undefined-type tags using an unterminated ASCII string and these
-// are exceptional in nature.
-//
-// 2. The presence of this method allows us to completely test the complimentary
-// no-nul parser.
-//
-func (ve *ValueEncoder) encodeAsciiNoNul(value string) (ed EncodedData, err error) {
- ed.Type = TypeAsciiNoNul
- ed.Encoded = []byte(value)
- ed.UnitCount = uint32(len(ed.Encoded))
-
- return ed, nil
-}
-
-func (ve *ValueEncoder) encodeShorts(value []uint16) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ed.UnitCount = uint32(len(value))
- ed.Encoded = make([]byte, ed.UnitCount*2)
-
- for i := uint32(0); i < ed.UnitCount; i++ {
- ve.byteOrder.PutUint16(ed.Encoded[i*2:(i+1)*2], value[i])
- }
-
- ed.Type = TypeShort
-
- return ed, nil
-}
-
-func (ve *ValueEncoder) encodeLongs(value []uint32) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ed.UnitCount = uint32(len(value))
- ed.Encoded = make([]byte, ed.UnitCount*4)
-
- for i := uint32(0); i < ed.UnitCount; i++ {
- ve.byteOrder.PutUint32(ed.Encoded[i*4:(i+1)*4], value[i])
- }
-
- ed.Type = TypeLong
-
- return ed, nil
-}
-
-func (ve *ValueEncoder) encodeRationals(value []Rational) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ed.UnitCount = uint32(len(value))
- ed.Encoded = make([]byte, ed.UnitCount*8)
-
- for i := uint32(0); i < ed.UnitCount; i++ {
- ve.byteOrder.PutUint32(ed.Encoded[i*8+0:i*8+4], value[i].Numerator)
- ve.byteOrder.PutUint32(ed.Encoded[i*8+4:i*8+8], value[i].Denominator)
- }
-
- ed.Type = TypeRational
-
- return ed, nil
-}
-
-func (ve *ValueEncoder) encodeSignedLongs(value []int32) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ed.UnitCount = uint32(len(value))
-
- b := bytes.NewBuffer(make([]byte, 0, 8*ed.UnitCount))
-
- for i := uint32(0); i < ed.UnitCount; i++ {
- err := binary.Write(b, ve.byteOrder, value[i])
- log.PanicIf(err)
- }
-
- ed.Type = TypeSignedLong
- ed.Encoded = b.Bytes()
-
- return ed, nil
-}
-
-func (ve *ValueEncoder) encodeSignedRationals(value []SignedRational) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- ed.UnitCount = uint32(len(value))
-
- b := bytes.NewBuffer(make([]byte, 0, 8*ed.UnitCount))
-
- for i := uint32(0); i < ed.UnitCount; i++ {
- err := binary.Write(b, ve.byteOrder, value[i].Numerator)
- log.PanicIf(err)
-
- err = binary.Write(b, ve.byteOrder, value[i].Denominator)
- log.PanicIf(err)
- }
-
- ed.Type = TypeSignedRational
- ed.Encoded = b.Bytes()
-
- return ed, nil
-}
-
-// Encode returns bytes for the given value, infering type from the actual
-// value. This does not support `TypeAsciiNoNull` (all strings are encoded as
-// `TypeAscii`).
-func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): This is redundant with EncodeWithType. Refactor one to use the other.
-
- switch value.(type) {
- case []byte:
- ed, err = ve.encodeBytes(value.([]byte))
- log.PanicIf(err)
- case string:
- ed, err = ve.encodeAscii(value.(string))
- log.PanicIf(err)
- case []uint16:
- ed, err = ve.encodeShorts(value.([]uint16))
- log.PanicIf(err)
- case []uint32:
- ed, err = ve.encodeLongs(value.([]uint32))
- log.PanicIf(err)
- case []Rational:
- ed, err = ve.encodeRationals(value.([]Rational))
- log.PanicIf(err)
- case []int32:
- ed, err = ve.encodeSignedLongs(value.([]int32))
- log.PanicIf(err)
- case []SignedRational:
- ed, err = ve.encodeSignedRationals(value.([]SignedRational))
- log.PanicIf(err)
- default:
- log.Panicf("value not encodable: [%s] [%v]", reflect.TypeOf(value), value)
- }
-
- return ed, nil
-}
-
-// EncodeWithType returns bytes for the given value, using the given `TagType`
-// value to determine how to encode. This supports `TypeAsciiNoNul`.
-func (ve *ValueEncoder) EncodeWithType(tt TagType, value interface{}) (ed EncodedData, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): This is redundant with Encode. Refactor one to use the other.
-
- switch tt.Type() {
- case TypeByte:
- ed, err = ve.encodeBytes(value.([]byte))
- log.PanicIf(err)
- case TypeAscii:
- ed, err = ve.encodeAscii(value.(string))
- log.PanicIf(err)
- case TypeAsciiNoNul:
- ed, err = ve.encodeAsciiNoNul(value.(string))
- log.PanicIf(err)
- case TypeShort:
- ed, err = ve.encodeShorts(value.([]uint16))
- log.PanicIf(err)
- case TypeLong:
- ed, err = ve.encodeLongs(value.([]uint32))
- log.PanicIf(err)
- case TypeRational:
- ed, err = ve.encodeRationals(value.([]Rational))
- log.PanicIf(err)
- case TypeSignedLong:
- ed, err = ve.encodeSignedLongs(value.([]int32))
- log.PanicIf(err)
- case TypeSignedRational:
- ed, err = ve.encodeSignedRationals(value.([]SignedRational))
- log.PanicIf(err)
- default:
- log.Panicf("value not encodable (with type): %v [%v]", tt, value)
- }
-
- return ed, nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/LICENSE b/vendor/github.com/dsoprea/go-exif/v2/LICENSE
deleted file mode 100644
index 0b9358a3a..000000000
--- a/vendor/github.com/dsoprea/go-exif/v2/LICENSE
+++ /dev/null
@@ -1,9 +0,0 @@
-MIT LICENSE
-
-Copyright 2019 Dustin Oprea
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/dsoprea/go-exif/v2/ifd.go b/vendor/github.com/dsoprea/go-exif/v2/ifd.go
deleted file mode 100644
index 80872e624..000000000
--- a/vendor/github.com/dsoprea/go-exif/v2/ifd.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package exif
-
-import (
- "github.com/dsoprea/go-logging"
-
- "github.com/dsoprea/go-exif/v2/common"
-)
-
-// TODO(dustin): This file now exists for backwards-compatibility only.
-
-// NewIfdMapping returns a new IfdMapping struct.
-func NewIfdMapping() (ifdMapping *exifcommon.IfdMapping) {
- return exifcommon.NewIfdMapping()
-}
-
-// NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the
-// standard IFDs.
-func NewIfdMappingWithStandard() (ifdMapping *exifcommon.IfdMapping) {
- return exifcommon.NewIfdMappingWithStandard()
-}
-
-// LoadStandardIfds loads the standard IFDs into the mapping.
-func LoadStandardIfds(im *exifcommon.IfdMapping) (err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- err = exifcommon.LoadStandardIfds(im)
- log.PanicIf(err)
-
- return nil
-}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/.MODULE_ROOT b/vendor/github.com/dsoprea/go-exif/v3/.MODULE_ROOT
index e69de29bb..e69de29bb 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/.MODULE_ROOT
+++ b/vendor/github.com/dsoprea/go-exif/v3/.MODULE_ROOT
diff --git a/vendor/github.com/dsoprea/go-exif/LICENSE b/vendor/github.com/dsoprea/go-exif/v3/LICENSE
index 0b9358a3a..0b9358a3a 100644
--- a/vendor/github.com/dsoprea/go-exif/LICENSE
+++ b/vendor/github.com/dsoprea/go-exif/v3/LICENSE
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/ifd.go b/vendor/github.com/dsoprea/go-exif/v3/common/ifd.go
index 9b93f04d9..01886e966 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/ifd.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/ifd.go
@@ -59,20 +59,19 @@ func NewIfdMapping() (ifdMapping *IfdMapping) {
// NewIfdMappingWithStandard retruns a new IfdMapping struct preloaded with the
// standard IFDs.
-func NewIfdMappingWithStandard() (ifdMapping *IfdMapping) {
+func NewIfdMappingWithStandard() (ifdMapping *IfdMapping, err error) {
defer func() {
if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
+ err = log.Wrap(state.(error))
}
}()
im := NewIfdMapping()
- err := LoadStandardIfds(im)
+ err = LoadStandardIfds(im)
log.PanicIf(err)
- return im
+ return im, nil
}
// Get returns the node given the path slice.
@@ -650,10 +649,3 @@ var (
// Ifd1StandardIfdIdentity represents the IFD path for IFD1.
Ifd1StandardIfdIdentity = NewIfdIdentity(rootStandardIfd, IfdIdentityPart{"IFD", 1})
)
-
-var (
- IfdPathStandard = IfdStandardIfdIdentity
- IfdPathStandardExif = IfdExifStandardIfdIdentity
- IfdPathStandardExifIop = IfdExifIopStandardIfdIdentity
- IfdPathStandardGps = IfdGpsInfoStandardIfdIdentity
-)
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/parser.go b/vendor/github.com/dsoprea/go-exif/v3/common/parser.go
index bbdd8f53a..76e8ef425 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/parser.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/parser.go
@@ -2,6 +2,8 @@ package exifcommon
import (
"bytes"
+ "errors"
+ "math"
"encoding/binary"
@@ -12,6 +14,10 @@ var (
parserLogger = log.NewLogger("exifcommon.parser")
)
+var (
+ ErrParseFail = errors.New("parse failure")
+)
+
// Parser knows how to parse all well-defined, encoded EXIF types.
type Parser struct {
}
@@ -56,7 +62,18 @@ func (p *Parser) ParseAscii(data []byte, unitCount uint32) (value string, err er
if len(data) == 0 || data[count-1] != 0 {
s := string(data[:count])
- parserLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s)
+ parserLogger.Warningf(nil, "ASCII not terminated with NUL as expected: [%v]", s)
+
+ for i, c := range s {
+ if c > 127 {
+ // Binary
+
+ t := s[:i]
+ parserLogger.Warningf(nil, "ASCII also had binary characters. Truncating: [%v]->[%s]", s, t)
+
+ return t, nil
+ }
+ }
return s, nil
}
@@ -135,6 +152,50 @@ func (p *Parser) ParseLongs(data []byte, unitCount uint32, byteOrder binary.Byte
return value, nil
}
+// ParseFloats knows how to encode an encoded list of floats.
+func (p *Parser) ParseFloats(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []float32, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ count := int(unitCount)
+
+ if len(data) != (TypeFloat.Size() * count) {
+ log.Panic(ErrNotEnoughData)
+ }
+
+ value = make([]float32, count)
+ for i := 0; i < count; i++ {
+ value[i] = math.Float32frombits(byteOrder.Uint32(data[i*4 : (i+1)*4]))
+ }
+
+ return value, nil
+}
+
+// ParseDoubles knows how to encode an encoded list of doubles.
+func (p *Parser) ParseDoubles(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []float64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ count := int(unitCount)
+
+ if len(data) != (TypeDouble.Size() * count) {
+ log.Panic(ErrNotEnoughData)
+ }
+
+ value = make([]float64, count)
+ for i := 0; i < count; i++ {
+ value[i] = math.Float64frombits(byteOrder.Uint64(data[i*8 : (i+1)*8]))
+ }
+
+ return value, nil
+}
+
// ParseRationals knows how to parse an encoded list of unsigned rationals.
func (p *Parser) ParseRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []Rational, err error) {
defer func() {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/testing_common.go b/vendor/github.com/dsoprea/go-exif/v3/common/testing_common.go
index f04fa22b6..f04fa22b6 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/testing_common.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/testing_common.go
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/type.go b/vendor/github.com/dsoprea/go-exif/v3/common/type.go
index 86b38d044..e79bcb9a1 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/type.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/type.go
@@ -6,6 +6,7 @@ import (
"reflect"
"strconv"
"strings"
+ "unicode"
"encoding/binary"
@@ -63,6 +64,12 @@ const (
// TypeSignedRational describes an encoded list of signed rationals.
TypeSignedRational TagTypePrimitive = 10
+ // TypeFloat describes an encoded list of floats
+ TypeFloat TagTypePrimitive = 11
+
+ // TypeDouble describes an encoded list of doubles.
+ TypeDouble TagTypePrimitive = 12
+
// TypeAsciiNoNul is just a pseudo-type, for our own purposes.
TypeAsciiNoNul TagTypePrimitive = 0xf0
)
@@ -74,23 +81,19 @@ func (typeType TagTypePrimitive) String() string {
// Size returns the size of one atomic unit of the type.
func (tagType TagTypePrimitive) Size() int {
- if tagType == TypeByte {
- return 1
- } else if tagType == TypeAscii || tagType == TypeAsciiNoNul {
+ switch tagType {
+ case TypeByte, TypeAscii, TypeAsciiNoNul:
return 1
- } else if tagType == TypeShort {
+ case TypeShort:
return 2
- } else if tagType == TypeLong {
+ case TypeLong, TypeSignedLong, TypeFloat:
return 4
- } else if tagType == TypeRational {
+ case TypeRational, TypeSignedRational, TypeDouble:
return 8
- } else if tagType == TypeSignedLong {
- return 4
- } else if tagType == TypeSignedRational {
- return 8
- } else {
- log.Panicf("can not determine tag-value size for type (%d): [%s]", tagType, TypeNames[tagType])
-
+ default:
+ log.Panicf("can not determine tag-value size for type (%d): [%s]",
+ tagType,
+ TypeNames[tagType])
// Never called.
return 0
}
@@ -109,6 +112,8 @@ func (tagType TagTypePrimitive) IsValid() bool {
tagType == TypeRational ||
tagType == TypeSignedLong ||
tagType == TypeSignedRational ||
+ tagType == TypeFloat ||
+ tagType == TypeDouble ||
tagType == TypeUndefined
}
@@ -123,6 +128,8 @@ var (
TypeUndefined: "UNDEFINED",
TypeSignedLong: "SLONG",
TypeSignedRational: "SRATIONAL",
+ TypeFloat: "FLOAT",
+ TypeDouble: "DOUBLE",
TypeAsciiNoNul: "_ASCII_NO_NUL",
}
@@ -148,6 +155,19 @@ type SignedRational struct {
Denominator int32
}
+func isPrintableText(s string) bool {
+ for _, c := range s {
+ // unicode.IsPrint() returns false for newline characters.
+ if c == 0x0d || c == 0x0a {
+ continue
+ } else if unicode.IsPrint(rune(c)) == false {
+ return false
+ }
+ }
+
+ return true
+}
+
// Format returns a stringified value for the given encoding. Automatically
// parses. Automatically calculates count based on type size. This function
// also supports undefined-type values (the ones that we support, anyway) by
@@ -166,37 +186,36 @@ func FormatFromType(value interface{}, justFirst bool) (phrase string, err error
case []byte:
return DumpBytesToString(t), nil
case string:
- return t, nil
- case []uint16:
- if len(t) == 0 {
- return "", nil
- }
-
- if justFirst == true {
- var valueSuffix string
- if len(t) > 1 {
- valueSuffix = "..."
+ for i, c := range t {
+ if c == 0 {
+ t = t[:i]
+ break
}
+ }
- return fmt.Sprintf("%v%s", t[0], valueSuffix), nil
+ if isPrintableText(t) == false {
+ phrase = fmt.Sprintf("string with binary data (%d bytes)", len(t))
+ return phrase, nil
}
- return fmt.Sprintf("%v", t), nil
- case []uint32:
- if len(t) == 0 {
+ return t, nil
+ case []uint16, []uint32, []int32, []float64, []float32:
+ val := reflect.ValueOf(t)
+
+ if val.Len() == 0 {
return "", nil
}
if justFirst == true {
var valueSuffix string
- if len(t) > 1 {
+ if val.Len() > 1 {
valueSuffix = "..."
}
- return fmt.Sprintf("%v%s", t[0], valueSuffix), nil
+ return fmt.Sprintf("%v%s", val.Index(0), valueSuffix), nil
}
- return fmt.Sprintf("%v", t), nil
+ return fmt.Sprintf("%v", val), nil
case []Rational:
if len(t) == 0 {
return "", nil
@@ -221,21 +240,6 @@ func FormatFromType(value interface{}, justFirst bool) (phrase string, err error
}
return fmt.Sprintf("%v", parts), nil
- case []int32:
- if len(t) == 0 {
- return "", nil
- }
-
- if justFirst == true {
- var valueSuffix string
- if len(t) > 1 {
- valueSuffix = "..."
- }
-
- return fmt.Sprintf("%v%s", t[0], valueSuffix), nil
- }
-
- return fmt.Sprintf("%v", t), nil
case []SignedRational:
if len(t) == 0 {
return "", nil
@@ -261,8 +265,14 @@ func FormatFromType(value interface{}, justFirst bool) (phrase string, err error
return fmt.Sprintf("%v", parts), nil
case fmt.Stringer:
+ s := t.String()
+ if isPrintableText(s) == false {
+ phrase = fmt.Sprintf("stringable with binary data (%d bytes)", len(s))
+ return phrase, nil
+ }
+
// An undefined value that is documented (or that we otherwise support).
- return t.String(), nil
+ return s, nil
default:
// Affects only "unknown" values, in general.
log.Panicf("type can not be formatted into string: %v", reflect.TypeOf(value).Name())
@@ -323,6 +333,16 @@ func FormatFromBytes(rawBytes []byte, tagType TagTypePrimitive, justFirst bool,
value, err = parser.ParseLongs(rawBytes, unitCount, byteOrder)
log.PanicIf(err)
+ case TypeFloat:
+ var err error
+
+ value, err = parser.ParseFloats(rawBytes, unitCount, byteOrder)
+ log.PanicIf(err)
+ case TypeDouble:
+ var err error
+
+ value, err = parser.ParseDoubles(rawBytes, unitCount, byteOrder)
+ log.PanicIf(err)
case TypeRational:
var err error
@@ -407,6 +427,16 @@ func TranslateStringToType(tagType TagTypePrimitive, valueString string) (value
log.PanicIf(err)
return int32(n), nil
+ } else if tagType == TypeFloat {
+ n, err := strconv.ParseFloat(valueString, 32)
+ log.PanicIf(err)
+
+ return float32(n), nil
+ } else if tagType == TypeDouble {
+ n, err := strconv.ParseFloat(valueString, 64)
+ log.PanicIf(err)
+
+ return float64(n), nil
} else if tagType == TypeSignedRational {
parts := strings.SplitN(valueString, "/", 2)
diff --git a/vendor/github.com/dsoprea/go-exif/utility.go b/vendor/github.com/dsoprea/go-exif/v3/common/utility.go
index 3d1ea2489..575049706 100644
--- a/vendor/github.com/dsoprea/go-exif/utility.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/utility.go
@@ -1,8 +1,9 @@
-package exif
+package exifcommon
import (
"bytes"
"fmt"
+ "reflect"
"strconv"
"strings"
"time"
@@ -10,6 +11,11 @@ import (
"github.com/dsoprea/go-logging"
)
+var (
+ timeType = reflect.TypeOf(time.Time{})
+)
+
+// DumpBytes prints a list of hex-encoded bytes.
func DumpBytes(data []byte) {
fmt.Printf("DUMP: ")
for _, x := range data {
@@ -19,6 +25,8 @@ func DumpBytes(data []byte) {
fmt.Printf("\n")
}
+// DumpBytesClause prints a list like DumpBytes(), but encapsulated in
+// "[]byte { ... }".
func DumpBytesClause(data []byte) {
fmt.Printf("DUMP: ")
@@ -35,6 +43,7 @@ func DumpBytesClause(data []byte) {
fmt.Printf(" }\n")
}
+// DumpBytesToString returns a stringified list of hex-encoded bytes.
func DumpBytesToString(data []byte) string {
b := new(bytes.Buffer)
@@ -51,6 +60,7 @@ func DumpBytesToString(data []byte) string {
return b.String()
}
+// DumpBytesClauseToString returns a comma-separated list of hex-encoded bytes.
func DumpBytesClauseToString(data []byte) string {
b := new(bytes.Buffer)
@@ -67,6 +77,14 @@ func DumpBytesClauseToString(data []byte) string {
return b.String()
}
+// ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a
+// `time.Time` struct. It will attempt to convert to UTC first.
+func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
+ t = t.UTC()
+
+ return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
+}
+
// ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC
// `time.Time` struct.
func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error) {
@@ -79,6 +97,10 @@ func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, er
parts := strings.Split(fullTimestampPhrase, " ")
datestampValue, timestampValue := parts[0], parts[1]
+ // Normalize the separators.
+ datestampValue = strings.ReplaceAll(datestampValue, "-", ":")
+ timestampValue = strings.ReplaceAll(timestampValue, "-", ":")
+
dateParts := strings.Split(datestampValue, ":")
year, err := strconv.ParseUint(dateParts[0], 10, 16)
@@ -117,106 +139,10 @@ func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, er
return timestamp, nil
}
-// ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a
-// `time.Time` struct. It will attempt to convert to UTC first.
-func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
- t = t.UTC()
-
- return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
-}
-
-// ExifTag is one simple representation of a tag in a flat list of all of them.
-type ExifTag struct {
- IfdPath string `json:"ifd_path"`
+// IsTime returns true if the value is a `time.Time`.
+func IsTime(v interface{}) bool {
- TagId uint16 `json:"id"`
- TagName string `json:"name"`
-
- TagTypeId TagTypePrimitive `json:"type_id"`
- TagTypeName string `json:"type_name"`
- Value interface{} `json:"value"`
- ValueBytes []byte `json:"value_bytes"`
-
- ChildIfdPath string `json:"child_ifd_path"`
-}
-
-// String returns a string representation.
-func (et ExifTag) String() string {
- return fmt.Sprintf("ExifTag<IFD-PATH=[%s] TAG-ID=(0x%02x) TAG-NAME=[%s] TAG-TYPE=[%s] VALUE=[%v] VALUE-BYTES=(%d) CHILD-IFD-PATH=[%s]", et.IfdPath, et.TagId, et.TagName, et.TagTypeName, et.Value, len(et.ValueBytes), et.ChildIfdPath)
-}
-
-// GetFlatExifData returns a simple, flat representation of all tags.
-func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- im := NewIfdMappingWithStandard()
- ti := NewTagIndex()
-
- _, index, err := Collect(im, ti, exifData)
- log.PanicIf(err)
-
- q := []*Ifd{index.RootIfd}
-
- exifTags = make([]ExifTag, 0)
-
- for len(q) > 0 {
- var ifd *Ifd
- ifd, q = q[0], q[1:]
-
- ti := NewTagIndex()
- for _, ite := range ifd.Entries {
- tagName := ""
-
- it, err := ti.Get(ifd.IfdPath, ite.TagId)
- if err != nil {
- // If it's a non-standard tag, just leave the name blank.
- if log.Is(err, ErrTagNotFound) != true {
- log.PanicIf(err)
- }
- } else {
- tagName = it.Name
- }
-
- value, err := ifd.TagValue(ite)
- if err != nil {
- if err == ErrUnhandledUnknownTypedTag {
- value = UnparseableUnknownTagValuePlaceholder
- } else {
- log.Panic(err)
- }
- }
-
- valueBytes, err := ifd.TagValueBytes(ite)
- if err != nil && err != ErrUnhandledUnknownTypedTag {
- log.Panic(err)
- }
-
- et := ExifTag{
- IfdPath: ifd.IfdPath,
- TagId: ite.TagId,
- TagName: tagName,
- TagTypeId: ite.TagType,
- TagTypeName: TypeNames[ite.TagType],
- Value: value,
- ValueBytes: valueBytes,
- ChildIfdPath: ite.ChildIfdPath,
- }
-
- exifTags = append(exifTags, et)
- }
-
- for _, childIfd := range ifd.Children {
- q = append(q, childIfd)
- }
-
- if ifd.NextIfd != nil {
- q = append(q, ifd.NextIfd)
- }
- }
+ // TODO(dustin): Add test
- return exifTags, nil
+ return reflect.TypeOf(v) == timeType
}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/value_context.go b/vendor/github.com/dsoprea/go-exif/v3/common/value_context.go
index feb078ccf..b9e634106 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/value_context.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/value_context.go
@@ -2,6 +2,7 @@ package exifcommon
import (
"errors"
+ "io"
"encoding/binary"
@@ -21,10 +22,10 @@ var (
// ValueContext embeds all of the parameters required to find and extract the
// actual tag value.
type ValueContext struct {
- unitCount uint32
- valueOffset uint32
- rawValueOffset []byte
- addressableData []byte
+ unitCount uint32
+ valueOffset uint32
+ rawValueOffset []byte
+ rs io.ReadSeeker
tagType TagTypePrimitive
byteOrder binary.ByteOrder
@@ -40,12 +41,12 @@ type ValueContext struct {
// TODO(dustin): We can update newValueContext() to derive `valueOffset` itself (from `rawValueOffset`).
// NewValueContext returns a new ValueContext struct.
-func NewValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
+func NewValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset []byte, rs io.ReadSeeker, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
return &ValueContext{
- unitCount: unitCount,
- valueOffset: valueOffset,
- rawValueOffset: rawValueOffset,
- addressableData: addressableData,
+ unitCount: unitCount,
+ valueOffset: valueOffset,
+ rawValueOffset: rawValueOffset,
+ rs: rs,
tagType: tagType,
byteOrder: byteOrder,
@@ -82,8 +83,11 @@ func (vc *ValueContext) RawValueOffset() []byte {
}
// AddressableData returns the block of data that we can dereference into.
-func (vc *ValueContext) AddressableData() []byte {
- return vc.addressableData
+func (vc *ValueContext) AddressableData() io.ReadSeeker {
+
+ // RELEASE)dustin): Rename from AddressableData() to ReadSeeker()
+
+ return vc.rs
}
// ByteOrder returns the byte-order of numbers.
@@ -152,7 +156,15 @@ func (vc *ValueContext) readRawEncoded() (rawBytes []byte, err error) {
return vc.rawValueOffset[:byteLength], nil
}
- return vc.addressableData[vc.valueOffset : vc.valueOffset+vc.unitCount*unitSizeRaw], nil
+ _, err = vc.rs.Seek(int64(vc.valueOffset), io.SeekStart)
+ log.PanicIf(err)
+
+ rawBytes = make([]byte, vc.unitCount*unitSizeRaw)
+
+ _, err = io.ReadFull(vc.rs, rawBytes)
+ log.PanicIf(err)
+
+ return rawBytes, nil
}
// GetFarOffset returns the offset if the value is not embedded [within the
@@ -303,6 +315,40 @@ func (vc *ValueContext) ReadLongs() (value []uint32, err error) {
return value, nil
}
+// ReadFloats parses the list of encoded, floats from the value-context.
+func (vc *ValueContext) ReadFloats() (value []float32, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ rawValue, err := vc.readRawEncoded()
+ log.PanicIf(err)
+
+ value, err = parser.ParseFloats(rawValue, vc.unitCount, vc.byteOrder)
+ log.PanicIf(err)
+
+ return value, nil
+}
+
+// ReadDoubles parses the list of encoded, doubles from the value-context.
+func (vc *ValueContext) ReadDoubles() (value []float64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ rawValue, err := vc.readRawEncoded()
+ log.PanicIf(err)
+
+ value, err = parser.ParseDoubles(rawValue, vc.unitCount, vc.byteOrder)
+ log.PanicIf(err)
+
+ return value, nil
+}
+
// ReadRationals parses the list of encoded, unsigned rationals from the value-
// context.
func (vc *ValueContext) ReadRationals() (value []Rational, err error) {
@@ -393,6 +439,12 @@ func (vc *ValueContext) Values() (values interface{}, err error) {
} else if vc.tagType == TypeSignedRational {
values, err = vc.ReadSignedRationals()
log.PanicIf(err)
+ } else if vc.tagType == TypeFloat {
+ values, err = vc.ReadFloats()
+ log.PanicIf(err)
+ } else if vc.tagType == TypeDouble {
+ values, err = vc.ReadDoubles()
+ log.PanicIf(err)
} else if vc.tagType == TypeUndefined {
log.Panicf("will not parse undefined-type value")
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/value_encoder.go b/vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go
index 52e0eacfd..2cd26cc7b 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/value_encoder.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go
@@ -2,6 +2,7 @@ package exifcommon
import (
"bytes"
+ "math"
"reflect"
"time"
@@ -113,6 +114,44 @@ func (ve *ValueEncoder) encodeLongs(value []uint32) (ed EncodedData, err error)
return ed, nil
}
+func (ve *ValueEncoder) encodeFloats(value []float32) (ed EncodedData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ ed.UnitCount = uint32(len(value))
+ ed.Encoded = make([]byte, ed.UnitCount*4)
+
+ for i := uint32(0); i < ed.UnitCount; i++ {
+ ve.byteOrder.PutUint32(ed.Encoded[i*4:(i+1)*4], math.Float32bits(value[i]))
+ }
+
+ ed.Type = TypeFloat
+
+ return ed, nil
+}
+
+func (ve *ValueEncoder) encodeDoubles(value []float64) (ed EncodedData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ ed.UnitCount = uint32(len(value))
+ ed.Encoded = make([]byte, ed.UnitCount*8)
+
+ for i := uint32(0); i < ed.UnitCount; i++ {
+ ve.byteOrder.PutUint64(ed.Encoded[i*8:(i+1)*8], math.Float64bits(value[i]))
+ }
+
+ ed.Type = TypeDouble
+
+ return ed, nil
+}
+
func (ve *ValueEncoder) encodeRationals(value []Rational) (ed EncodedData, err error) {
defer func() {
if state := recover(); state != nil {
@@ -190,33 +229,38 @@ func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error) {
}
}()
- switch value.(type) {
+ switch t := value.(type) {
case []byte:
- ed, err = ve.encodeBytes(value.([]byte))
+ ed, err = ve.encodeBytes(t)
log.PanicIf(err)
case string:
- ed, err = ve.encodeAscii(value.(string))
+ ed, err = ve.encodeAscii(t)
log.PanicIf(err)
case []uint16:
- ed, err = ve.encodeShorts(value.([]uint16))
+ ed, err = ve.encodeShorts(t)
log.PanicIf(err)
case []uint32:
- ed, err = ve.encodeLongs(value.([]uint32))
+ ed, err = ve.encodeLongs(t)
+ log.PanicIf(err)
+ case []float32:
+ ed, err = ve.encodeFloats(t)
+ log.PanicIf(err)
+ case []float64:
+ ed, err = ve.encodeDoubles(t)
log.PanicIf(err)
case []Rational:
- ed, err = ve.encodeRationals(value.([]Rational))
+ ed, err = ve.encodeRationals(t)
log.PanicIf(err)
case []int32:
- ed, err = ve.encodeSignedLongs(value.([]int32))
+ ed, err = ve.encodeSignedLongs(t)
log.PanicIf(err)
case []SignedRational:
- ed, err = ve.encodeSignedRationals(value.([]SignedRational))
+ ed, err = ve.encodeSignedRationals(t)
log.PanicIf(err)
case time.Time:
// For convenience, if the user doesn't want to deal with translation
// semantics with timestamps.
- t := value.(time.Time)
s := ExifFullTimestampString(t)
ed, err = ve.encodeAscii(s)
diff --git a/vendor/github.com/dsoprea/go-exif/v3/data_layer.go b/vendor/github.com/dsoprea/go-exif/v3/data_layer.go
new file mode 100644
index 000000000..7883752cc
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-exif/v3/data_layer.go
@@ -0,0 +1,50 @@
+package exif
+
+import (
+ "io"
+
+ "github.com/dsoprea/go-logging"
+ "github.com/dsoprea/go-utility/v2/filesystem"
+)
+
+type ExifBlobSeeker interface {
+ GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error)
+}
+
+// ExifReadSeeker knows how to retrieve data from the EXIF blob relative to the
+// beginning of the blob (so, absolute position (0) is the first byte of the
+// EXIF data).
+type ExifReadSeeker struct {
+ rs io.ReadSeeker
+}
+
+func NewExifReadSeeker(rs io.ReadSeeker) *ExifReadSeeker {
+ return &ExifReadSeeker{
+ rs: rs,
+ }
+}
+
+func NewExifReadSeekerWithBytes(exifData []byte) *ExifReadSeeker {
+ sb := rifs.NewSeekableBufferWithBytes(exifData)
+ edbs := NewExifReadSeeker(sb)
+
+ return edbs
+}
+
+// Fork creates a new ReadSeeker instead that wraps a BouncebackReader to
+// maintain its own position in the stream.
+func (edbs *ExifReadSeeker) GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ br, err := rifs.NewBouncebackReader(edbs.rs)
+ log.PanicIf(err)
+
+ _, err = br.Seek(initialOffset, io.SeekStart)
+ log.PanicIf(err)
+
+ return br, nil
+}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/error.go b/vendor/github.com/dsoprea/go-exif/v3/error.go
index 2f00b08a4..2f00b08a4 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/error.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/error.go
diff --git a/vendor/github.com/dsoprea/go-exif/v2/exif.go b/vendor/github.com/dsoprea/go-exif/v3/exif.go
index 20b723769..f66e839d9 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/exif.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/exif.go
@@ -13,7 +13,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
const (
@@ -70,10 +70,58 @@ func SearchAndExtractExif(data []byte) (rawExif []byte, err error) {
return rawExif, nil
}
-// SearchAndExtractExifWithReader searches for an EXIF blob using an
-// `io.Reader`. We can't know how much long the EXIF data is without parsing it,
-// so this will likely grab up a lot of the image-data, too.
-func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error) {
+// SearchAndExtractExifN searches for an EXIF blob in the byte-slice, but skips
+// the given number of EXIF blocks first. This is a forensics tool that helps
+// identify multiple EXIF blocks in a file.
+func SearchAndExtractExifN(data []byte, n int) (rawExif []byte, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ skips := 0
+ totalDiscarded := 0
+ for {
+ b := bytes.NewBuffer(data)
+
+ var discarded int
+
+ rawExif, discarded, err = searchAndExtractExifWithReaderWithDiscarded(b)
+ if err != nil {
+ if err == ErrNoExif {
+ return nil, err
+ }
+
+ log.Panic(err)
+ }
+
+ exifLogger.Debugf(nil, "Read EXIF block (%d).", skips)
+
+ totalDiscarded += discarded
+
+ if skips >= n {
+ exifLogger.Debugf(nil, "Reached requested EXIF block (%d).", n)
+ break
+ }
+
+ nextOffset := discarded + 1
+ exifLogger.Debugf(nil, "Skipping EXIF block (%d) by seeking to position (%d).", skips, nextOffset)
+
+ data = data[nextOffset:]
+ skips++
+ }
+
+ exifLogger.Debugf(nil, "Found EXIF blob (%d) bytes from initial position.", totalDiscarded)
+ return rawExif, nil
+}
+
+// searchAndExtractExifWithReaderWithDiscarded searches for an EXIF blob using
+// an `io.Reader`. We can't know how much long the EXIF data is without parsing
+// it, so this will likely grab up a lot of the image-data, too.
+//
+// This function returned the count of preceding bytes.
+func searchAndExtractExifWithReaderWithDiscarded(r io.Reader) (rawExif []byte, discarded int, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@@ -85,13 +133,12 @@ func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error) {
// least, again, with JPEGs).
br := bufio.NewReader(r)
- discarded := 0
for {
window, err := br.Peek(ExifSignatureLength)
if err != nil {
if err == io.EOF {
- return nil, ErrNoExif
+ return nil, 0, ErrNoExif
}
log.Panic(err)
@@ -122,6 +169,30 @@ func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error) {
rawExif, err = ioutil.ReadAll(br)
log.PanicIf(err)
+ return rawExif, discarded, nil
+}
+
+// RELEASE(dustin): We should replace the implementation of SearchAndExtractExifWithReader with searchAndExtractExifWithReaderWithDiscarded and drop the latter.
+
+// SearchAndExtractExifWithReader searches for an EXIF blob using an
+// `io.Reader`. We can't know how much long the EXIF data is without parsing it,
+// so this will likely grab up a lot of the image-data, too.
+func SearchAndExtractExifWithReader(r io.Reader) (rawExif []byte, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ rawExif, _, err = searchAndExtractExifWithReaderWithDiscarded(r)
+ if err != nil {
+ if err == ErrNoExif {
+ return nil, err
+ }
+
+ log.Panic(err)
+ }
+
return rawExif, nil
}
@@ -179,9 +250,11 @@ func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
}
if bytes.Equal(data[:4], ExifBigEndianSignature[:]) == true {
+ exifLogger.Debugf(nil, "Byte-order is big-endian.")
eh.ByteOrder = binary.BigEndian
} else if bytes.Equal(data[:4], ExifLittleEndianSignature[:]) == true {
eh.ByteOrder = binary.LittleEndian
+ exifLogger.Debugf(nil, "Byte-order is little-endian.")
} else {
return eh, ErrNoExif
}
@@ -192,7 +265,7 @@ func ParseExifHeader(data []byte) (eh ExifHeader, err error) {
}
// Visit recursively invokes a callback for every tag.
-func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn) (eh ExifHeader, furthestOffset uint32, err error) {
+func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, visitor TagVisitorFn, so *ScanOptions) (eh ExifHeader, furthestOffset uint32, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@@ -202,9 +275,10 @@ func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMa
eh, err = ParseExifHeader(exifData)
log.PanicIf(err)
- ie := NewIfdEnumerate(ifdMapping, tagIndex, exifData, eh.ByteOrder)
+ ebs := NewExifReadSeekerWithBytes(exifData)
+ ie := NewIfdEnumerate(ifdMapping, tagIndex, ebs, eh.ByteOrder)
- _, err = ie.Scan(rootIfdIdentity, eh.FirstIfdOffset, visitor)
+ _, err = ie.Scan(rootIfdIdentity, eh.FirstIfdOffset, visitor, so)
log.PanicIf(err)
furthestOffset = ie.FurthestOffset()
@@ -223,7 +297,8 @@ func Collect(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []b
eh, err = ParseExifHeader(exifData)
log.PanicIf(err)
- ie := NewIfdEnumerate(ifdMapping, tagIndex, exifData, eh.ByteOrder)
+ ebs := NewExifReadSeekerWithBytes(exifData)
+ ie := NewIfdEnumerate(ifdMapping, tagIndex, ebs, eh.ByteOrder)
index, err = ie.Collect(eh.FirstIfdOffset)
log.PanicIf(err)
diff --git a/vendor/github.com/dsoprea/go-exif/v2/gps.go b/vendor/github.com/dsoprea/go-exif/v3/gps.go
index d44ede1ad..7a61cd94d 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/gps.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/gps.go
@@ -8,7 +8,7 @@ import (
"github.com/dsoprea/go-logging"
"github.com/golang/geo/s2"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
var (
diff --git a/vendor/github.com/dsoprea/go-exif/v2/ifd_builder.go b/vendor/github.com/dsoprea/go-exif/v3/ifd_builder.go
index 64a09299c..a404b362a 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/ifd_builder.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/ifd_builder.go
@@ -14,8 +14,8 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
- "github.com/dsoprea/go-exif/v2/undefined"
+ "github.com/dsoprea/go-exif/v3/common"
+ "github.com/dsoprea/go-exif/v3/undefined"
)
var (
@@ -262,8 +262,8 @@ func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
ib = &IfdBuilder{
ifdIdentity: ifd.IfdIdentity(),
- byteOrder: ifd.ByteOrder,
- existingOffset: ifd.Offset,
+ byteOrder: ifd.ByteOrder(),
+ existingOffset: ifd.Offset(),
ifdMapping: ifd.ifdMapping,
tagIndex: ifd.tagIndex,
}
@@ -276,12 +276,12 @@ func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) {
func NewIfdBuilderFromExistingChain(rootIfd *Ifd) (firstIb *IfdBuilder) {
var lastIb *IfdBuilder
i := 0
- for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.NextIfd {
+ for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.nextIfd {
newIb := NewIfdBuilder(
rootIfd.ifdMapping,
rootIfd.tagIndex,
rootIfd.ifdIdentity,
- thisExistingIfd.ByteOrder)
+ thisExistingIfd.ByteOrder())
if firstIb == nil {
firstIb = newIb
@@ -1005,7 +1005,7 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excl
log.Panic(err)
}
- for i, ite := range ifd.Entries {
+ for i, ite := range ifd.Entries() {
if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() {
// These will be added on-the-fly when we encode.
continue
@@ -1051,11 +1051,11 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excl
// this IFD represents this specific child IFD.
var childIfd *Ifd
- for _, thisChildIfd := range ifd.Children {
- if thisChildIfd.ParentTagIndex != i {
+ for _, thisChildIfd := range ifd.Children() {
+ if thisChildIfd.ParentTagIndex() != i {
continue
} else if thisChildIfd.ifdIdentity.TagId() != 0xffff && thisChildIfd.ifdIdentity.TagId() != ite.TagId() {
- log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex, ite, thisChildIfd)
+ log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex(), ite, thisChildIfd)
}
childIfd = thisChildIfd
@@ -1063,9 +1063,9 @@ func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excl
}
if childIfd == nil {
- childTagIds := make([]string, len(ifd.Children))
- for j, childIfd := range ifd.Children {
- childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.ifdIdentity.TagId(), childIfd.ParentTagIndex)
+ childTagIds := make([]string, len(ifd.Children()))
+ for j, childIfd := range ifd.Children() {
+ childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.ifdIdentity.TagId(), childIfd.ParentTagIndex())
}
log.Panicf("could not find child IFD for child ITE: IFD-PATH=[%s] TAG-ID=(0x%04x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.IfdPath(), ite.TagId(), i, childTagIds)
diff --git a/vendor/github.com/dsoprea/go-exif/v2/ifd_builder_encode.go b/vendor/github.com/dsoprea/go-exif/v3/ifd_builder_encode.go
index a0bac3e5b..a0f4ff79c 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/ifd_builder_encode.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/ifd_builder_encode.go
@@ -9,7 +9,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
const (
diff --git a/vendor/github.com/dsoprea/go-exif/v2/ifd_enumerate.go b/vendor/github.com/dsoprea/go-exif/v3/ifd_enumerate.go
index 33a5f84b3..3167596ef 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/ifd_enumerate.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/ifd_enumerate.go
@@ -13,8 +13,8 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
- "github.com/dsoprea/go-exif/v2/undefined"
+ "github.com/dsoprea/go-exif/v3/common"
+ "github.com/dsoprea/go-exif/v3/undefined"
)
var (
@@ -75,23 +75,23 @@ var (
// statically-sized records. So, the tags (though notnecessarily their values)
// are fairly simple to enumerate.
type byteParser struct {
- byteOrder binary.ByteOrder
- addressableData []byte
- ifdOffset uint32
- currentOffset uint32
+ byteOrder binary.ByteOrder
+ rs io.ReadSeeker
+ ifdOffset uint32
+ currentOffset uint32
}
-func newByteParser(addressableData []byte, byteOrder binary.ByteOrder, ifdOffset uint32) (bp *byteParser, err error) {
- if ifdOffset >= uint32(len(addressableData)) {
- return nil, ErrOffsetInvalid
- }
-
+// newByteParser returns a new byteParser struct.
+//
+// initialOffset is for arithmetic-based tracking of where we should be at in
+// the stream.
+func newByteParser(rs io.ReadSeeker, byteOrder binary.ByteOrder, initialOffset uint32) (bp *byteParser, err error) {
// TODO(dustin): Add test
bp = &byteParser{
- addressableData: addressableData,
- byteOrder: byteOrder,
- currentOffset: ifdOffset,
+ rs: rs,
+ byteOrder: byteOrder,
+ currentOffset: initialOffset,
}
return bp, nil
@@ -109,13 +109,13 @@ func (bp *byteParser) getUint16() (value uint16, raw []byte, err error) {
// TODO(dustin): Add test
- needBytes := uint32(2)
+ needBytes := 2
- if bp.currentOffset+needBytes > uint32(len(bp.addressableData)) {
- return 0, nil, io.EOF
- }
+ raw = make([]byte, needBytes)
+
+ _, err = io.ReadFull(bp.rs, raw)
+ log.PanicIf(err)
- raw = bp.addressableData[bp.currentOffset : bp.currentOffset+needBytes]
value = bp.byteOrder.Uint16(raw)
bp.currentOffset += uint32(needBytes)
@@ -135,13 +135,13 @@ func (bp *byteParser) getUint32() (value uint32, raw []byte, err error) {
// TODO(dustin): Add test
- needBytes := uint32(4)
+ needBytes := 4
- if bp.currentOffset+needBytes > uint32(len(bp.addressableData)) {
- return 0, nil, io.EOF
- }
+ raw = make([]byte, needBytes)
+
+ _, err = io.ReadFull(bp.rs, raw)
+ log.PanicIf(err)
- raw = bp.addressableData[bp.currentOffset : bp.currentOffset+needBytes]
value = bp.byteOrder.Uint32(raw)
bp.currentOffset += uint32(needBytes)
@@ -158,20 +158,24 @@ func (bp *byteParser) CurrentOffset() uint32 {
// IfdEnumerate is the main enumeration type. It knows how to parse the IFD
// containers in the EXIF blob.
type IfdEnumerate struct {
- exifData []byte
+ ebs ExifBlobSeeker
byteOrder binary.ByteOrder
tagIndex *TagIndex
ifdMapping *exifcommon.IfdMapping
furthestOffset uint32
+
+ visitedIfdOffsets map[uint32]struct{}
}
// NewIfdEnumerate returns a new instance of IfdEnumerate.
-func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate {
+func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ebs ExifBlobSeeker, byteOrder binary.ByteOrder) *IfdEnumerate {
return &IfdEnumerate{
- exifData: exifData,
+ ebs: ebs,
byteOrder: byteOrder,
ifdMapping: ifdMapping,
tagIndex: tagIndex,
+
+ visitedIfdOffsets: make(map[uint32]struct{}),
}
}
@@ -182,11 +186,16 @@ func (ie *IfdEnumerate) getByteParser(ifdOffset uint32) (bp *byteParser, err err
}
}()
+ initialOffset := ExifAddressableAreaStart + ifdOffset
+
+ rs, err := ie.ebs.GetReadSeeker(int64(initialOffset))
+ log.PanicIf(err)
+
bp, err =
newByteParser(
- ie.exifData[ExifAddressableAreaStart:],
+ rs,
ie.byteOrder,
- ifdOffset)
+ initialOffset)
if err != nil {
if err == ErrOffsetInvalid {
@@ -220,15 +229,63 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp
valueOffset, rawValueOffset, err := bp.getUint32()
log.PanicIf(err)
+ // Check whether the embedded type indicator is valid.
+
if tagType.IsValid() == false {
+ // Technically, we have the type on-file in the tags-index, but
+ // if the type stored alongside the data disagrees with it,
+ // which it apparently does, all bets are off.
+ ifdEnumerateLogger.Warningf(nil,
+ "Tag (0x%04x) in IFD [%s] at position (%d) has invalid type (0x%04x) and will be skipped.",
+ tagId, ii, tagPosition, int(tagType))
+
ite = &IfdTagEntry{
tagId: tagId,
tagType: tagType,
}
- log.Panic(ErrTagTypeNotValid)
+ return ite, ErrTagTypeNotValid
+ }
+
+ // Check whether the embedded type is listed among the supported types for
+ // the registered tag. If not, skip processing the tag.
+
+ it, err := ie.tagIndex.Get(ii, tagId)
+ if err != nil {
+ if log.Is(err, ErrTagNotFound) == true {
+ ifdEnumerateLogger.Warningf(nil, "Tag (0x%04x) is not known and will be skipped.", tagId)
+
+ ite = &IfdTagEntry{
+ tagId: tagId,
+ }
+
+ return ite, ErrTagNotFound
+ }
+
+ log.Panic(err)
}
+ // If we're trying to be as forgiving as possible then use whatever type was
+ // reported in the format. Otherwise, only accept a type that's expected for
+ // this tag.
+ if ie.tagIndex.UniversalSearch() == false && it.DoesSupportType(tagType) == false {
+ // The type in the stream disagrees with the type that this tag is
+ // expected to have. This can present issues with how we handle the
+ // special-case tags (e.g. thumbnails, GPS, etc..) when those tags
+ // suddenly have data that we no longer manipulate correctly/
+ // accurately.
+ ifdEnumerateLogger.Warningf(nil,
+ "Tag (0x%04x) in IFD [%s] at position (%d) has unsupported type (0x%02x) and will be skipped.",
+ tagId, ii, tagPosition, int(tagType))
+
+ return nil, ErrTagTypeNotValid
+ }
+
+ // Construct tag struct.
+
+ rs, err := ie.ebs.GetReadSeeker(0)
+ log.PanicIf(err)
+
ite = newIfdTagEntry(
ii,
tagId,
@@ -237,7 +294,7 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp
unitCount,
valueOffset,
rawValueOffset,
- ie.exifData[ExifAddressableAreaStart:],
+ rs,
ie.byteOrder)
ifdPath := ii.UnindexedString()
@@ -263,10 +320,10 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp
}
// TagVisitorFn is called for each tag when enumerating through the EXIF.
-type TagVisitorFn func(fqIfdPath string, ifdIndex int, ite *IfdTagEntry) (err error)
+type TagVisitorFn func(ite *IfdTagEntry) (err error)
-// postparseTag do some tag-level processing here following the parse of each.
-func (ie *IfdEnumerate) postparseTag(ite *IfdTagEntry, med *MiscellaneousExifData) (err error) {
+// tagPostParse do some tag-level processing here following the parse of each.
+func (ie *IfdEnumerate) tagPostParse(ite *IfdTagEntry, med *MiscellaneousExifData) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@@ -355,7 +412,7 @@ func (ie *IfdEnumerate) postparseTag(ite *IfdTagEntry, med *MiscellaneousExifDat
// tag should ever be repeated, and b) all but one had an incorrect
// type and caused parsing/conversion woes. So, this is a quick fix
// for those scenarios.
- if it.DoesSupportType(tagType) == false {
+ if ie.tagIndex.UniversalSearch() == false && it.DoesSupportType(tagType) == false {
ifdEnumerateLogger.Warningf(nil,
"Skipping tag [%s] (0x%04x) [%s] with an unexpected type: %v ∉ %v",
ii.UnindexedString(), tagId, it.Name,
@@ -389,18 +446,16 @@ func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, vis
for i := 0; i < int(tagCount); i++ {
ite, err := ie.parseTag(ii, i, bp)
if err != nil {
- if log.Is(err, ErrTagTypeNotValid) == true {
- // Technically, we have the type on-file in the tags-index, but
- // if the type stored alongside the data disagrees with it,
- // which it apparently does, all bets are off.
- ifdEnumerateLogger.Warningf(nil, "Tag (0x%04x) in IFD [%s] at position (%d) has invalid type (%d) and will be skipped.", ite.tagId, ii, i, ite.tagType)
+ if log.Is(err, ErrTagNotFound) == true || log.Is(err, ErrTagTypeNotValid) == true {
+ // These tags should've been fully logged in parseTag(). The
+ // ITE returned is nil so we can't print anything about them, now.
continue
}
log.Panic(err)
}
- err = ie.postparseTag(ite, med)
+ err = ie.tagPostParse(ite, med)
if err == nil {
if err == ErrTagNotFound {
continue
@@ -412,7 +467,7 @@ func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, vis
tagId := ite.TagId()
if visitor != nil {
- err := visitor(ii.String(), ii.Index(), ite)
+ err := visitor(ite)
log.PanicIf(err)
}
@@ -478,27 +533,43 @@ func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, vis
if enumeratorThumbnailOffset != nil && enumeratorThumbnailSize != nil {
thumbnailData, err = ie.parseThumbnail(enumeratorThumbnailOffset, enumeratorThumbnailSize)
- log.PanicIf(err)
-
- // In this case, the value is always an offset.
- offset := enumeratorThumbnailOffset.getValueOffset()
+ if err != nil {
+ ifdEnumerateLogger.Errorf(
+ nil, err,
+ "We tried to bump our furthest-offset counter but there was an issue first seeking past the thumbnail.")
+ } else {
+ // In this case, the value is always an offset.
+ offset := enumeratorThumbnailOffset.getValueOffset()
- // This this case, the value is always a length.
- length := enumeratorThumbnailSize.getValueOffset()
+ // This this case, the value is always a length.
+ length := enumeratorThumbnailSize.getValueOffset()
- ifdEnumerateLogger.Debugf(nil, "Found thumbnail in IFD [%s]. Its offset is (%d) and is (%d) bytes.", ii, offset, length)
+ ifdEnumerateLogger.Debugf(nil, "Found thumbnail in IFD [%s]. Its offset is (%d) and is (%d) bytes.", ii, offset, length)
- furthestOffset := offset + length
+ furthestOffset := offset + length
- if furthestOffset > ie.furthestOffset {
- ie.furthestOffset = furthestOffset
+ if furthestOffset > ie.furthestOffset {
+ ie.furthestOffset = furthestOffset
+ }
}
}
nextIfdOffset, _, err = bp.getUint32()
log.PanicIf(err)
- ifdEnumerateLogger.Debugf(nil, "Next IFD at offset: (%08x)", nextIfdOffset)
+ _, alreadyVisited := ie.visitedIfdOffsets[nextIfdOffset]
+
+ if alreadyVisited == true {
+ ifdEnumerateLogger.Warningf(nil, "IFD at offset (0x%08x) has been linked-to more than once. There might be a cycle in the IFD chain. Not reparsing.", nextIfdOffset)
+ nextIfdOffset = 0
+ }
+
+ if nextIfdOffset != 0 {
+ ie.visitedIfdOffsets[nextIfdOffset] = struct{}{}
+ ifdEnumerateLogger.Debugf(nil, "[%s] Next IFD at offset: (0x%08x)", ii.String(), nextIfdOffset)
+ } else {
+ ifdEnumerateLogger.Debugf(nil, "[%s] IFD chain has terminated.", ii.String())
+ }
return nextIfdOffset, entries, thumbnailData, nil
}
@@ -587,9 +658,14 @@ func (med *MiscellaneousExifData) UnknownTags() map[exifcommon.BasicTag]exifcomm
return med.unknownTags
}
+// ScanOptions tweaks parser behavior/choices.
+type ScanOptions struct {
+ // NOTE(dustin): Reserved for future usage.
+}
+
// Scan enumerates the different EXIF blocks (called IFDs). `rootIfdName` will
// be "IFD" in the TIFF standard.
-func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn) (med *MiscellaneousExifData, err error) {
+func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, visitor TagVisitorFn, so *ScanOptions) (med *MiscellaneousExifData, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@@ -612,38 +688,32 @@ func (ie *IfdEnumerate) Scan(iiRoot *exifcommon.IfdIdentity, ifdOffset uint32, v
// Ifd represents a single, parsed IFD.
type Ifd struct {
-
- // TODO(dustin): Add NextIfd().
-
ifdIdentity *exifcommon.IfdIdentity
- ByteOrder binary.ByteOrder
+ ifdMapping *exifcommon.IfdMapping
+ tagIndex *TagIndex
- Id int
+ offset uint32
+ byteOrder binary.ByteOrder
+ id int
- ParentIfd *Ifd
+ parentIfd *Ifd
// ParentTagIndex is our tag position in the parent IFD, if we had a parent
// (if `ParentIfd` is not nil and we weren't an IFD referenced as a sibling
// instead of as a child).
- ParentTagIndex int
-
- Offset uint32
+ parentTagIndex int
- Entries []*IfdTagEntry
- EntriesByTagId map[uint16][]*IfdTagEntry
+ entries []*IfdTagEntry
+ entriesByTagId map[uint16][]*IfdTagEntry
- Children []*Ifd
-
- ChildIfdIndex map[string]*Ifd
-
- NextIfdOffset uint32
- NextIfd *Ifd
+ children []*Ifd
+ childIfdIndex map[string]*Ifd
thumbnailData []byte
- ifdMapping *exifcommon.IfdMapping
- tagIndex *TagIndex
+ nextIfdOffset uint32
+ nextIfd *Ifd
}
// IfdIdentity returns IFD identity that this struct represents.
@@ -651,6 +721,71 @@ func (ifd *Ifd) IfdIdentity() *exifcommon.IfdIdentity {
return ifd.ifdIdentity
}
+// Entries returns a flat list of all tags for this IFD.
+func (ifd *Ifd) Entries() []*IfdTagEntry {
+
+ // TODO(dustin): Add test
+
+ return ifd.entries
+}
+
+// EntriesByTagId returns a map of all tags for this IFD.
+func (ifd *Ifd) EntriesByTagId() map[uint16][]*IfdTagEntry {
+
+ // TODO(dustin): Add test
+
+ return ifd.entriesByTagId
+}
+
+// Children returns a flat list of all child IFDs of this IFD.
+func (ifd *Ifd) Children() []*Ifd {
+
+ // TODO(dustin): Add test
+
+ return ifd.children
+}
+
+// ChildWithIfdPath returns a map of all child IFDs of this IFD.
+func (ifd *Ifd) ChildIfdIndex() map[string]*Ifd {
+
+ // TODO(dustin): Add test
+
+ return ifd.childIfdIndex
+}
+
+// ParentTagIndex returns the position of this IFD's tag in its parent IFD (*if*
+// there is a parent).
+func (ifd *Ifd) ParentTagIndex() int {
+
+ // TODO(dustin): Add test
+
+ return ifd.parentTagIndex
+}
+
+// Offset returns the offset of the IFD in the stream.
+func (ifd *Ifd) Offset() uint32 {
+
+ // TODO(dustin): Add test
+
+ return ifd.offset
+}
+
+// Offset returns the offset of the IFD in the stream.
+func (ifd *Ifd) ByteOrder() binary.ByteOrder {
+
+ // TODO(dustin): Add test
+
+ return ifd.byteOrder
+}
+
+// NextIfd returns the Ifd struct for the next IFD in the chain.
+func (ifd *Ifd) NextIfd() *Ifd {
+
+ // TODO(dustin): Add test
+
+ return ifd.nextIfd
+}
+
// ChildWithIfdPath returns an `Ifd` struct for the given child of the current
// IFD.
func (ifd *Ifd) ChildWithIfdPath(iiChild *exifcommon.IfdIdentity) (childIfd *Ifd, err error) {
@@ -663,7 +798,7 @@ func (ifd *Ifd) ChildWithIfdPath(iiChild *exifcommon.IfdIdentity) (childIfd *Ifd
// TODO(dustin): This is a bridge while we're introducing the IFD type-system. We should be able to use the (IfdIdentity).Equals() method for this.
ifdPath := iiChild.UnindexedString()
- for _, childIfd := range ifd.Children {
+ for _, childIfd := range ifd.children {
if childIfd.ifdIdentity.UnindexedString() == ifdPath {
return childIfd, nil
}
@@ -682,7 +817,7 @@ func (ifd *Ifd) FindTagWithId(tagId uint16) (results []*IfdTagEntry, err error)
}
}()
- results, found := ifd.EntriesByTagId[tagId]
+ results, found := ifd.entriesByTagId[tagId]
if found != true {
log.Panic(ErrTagNotFound)
}
@@ -707,7 +842,7 @@ func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err err
}
results = make([]*IfdTagEntry, 0)
- for _, ite := range ifd.Entries {
+ for _, ite := range ifd.entries {
if ite.TagId() == it.Id {
results = append(results, ite)
}
@@ -723,11 +858,11 @@ func (ifd *Ifd) FindTagWithName(tagName string) (results []*IfdTagEntry, err err
// String returns a description string.
func (ifd *Ifd) String() string {
parentOffset := uint32(0)
- if ifd.ParentIfd != nil {
- parentOffset = ifd.ParentIfd.Offset
+ if ifd.parentIfd != nil {
+ parentOffset = ifd.parentIfd.offset
}
- return fmt.Sprintf("Ifd<ID=(%d) IFD-PATH=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.Id, ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index(), len(ifd.Entries), ifd.Offset, len(ifd.Children), parentOffset, ifd.NextIfdOffset)
+ return fmt.Sprintf("Ifd<ID=(%d) IFD-PATH=[%s] INDEX=(%d) COUNT=(%d) OFF=(0x%04x) CHILDREN=(%d) PARENT=(0x%04x) NEXT-IFD=(0x%04x)>", ifd.id, ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index(), len(ifd.entries), ifd.offset, len(ifd.children), parentOffset, ifd.nextIfdOffset)
}
// Thumbnail returns the raw thumbnail bytes. This is typically directly
@@ -751,14 +886,14 @@ func (ifd *Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
ifdsFoundCount := 0
- for _, ite := range ifd.Entries {
+ for _, ite := range ifd.entries {
tags = append(tags, ite)
childIfdPath := ite.ChildIfdPath()
if childIfdPath != "" {
ifdsFoundCount++
- childIfd, found := ifd.ChildIfdIndex[childIfdPath]
+ childIfd, found := ifd.childIfdIndex[childIfdPath]
if found != true {
log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
}
@@ -767,12 +902,12 @@ func (ifd *Ifd) dumpTags(tags []*IfdTagEntry) []*IfdTagEntry {
}
}
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
+ if len(ifd.children) != ifdsFoundCount {
+ log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
}
- if ifd.NextIfd != nil {
- tags = ifd.NextIfd.dumpTags(tags)
+ if ifd.nextIfd != nil {
+ tags = ifd.nextIfd.dumpTags(tags)
}
return tags
@@ -797,7 +932,7 @@ func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink boo
ifdsFoundCount := 0
- for _, ite := range ifd.Entries {
+ for _, ite := range ifd.entries {
if ite.ChildIfdPath() != "" {
fmt.Printf("%s - TAG: %s\n", indent, ite)
} else {
@@ -841,7 +976,7 @@ func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink boo
if childIfdPath != "" {
ifdsFoundCount++
- childIfd, found := ifd.ChildIfdIndex[childIfdPath]
+ childIfd, found := ifd.childIfdIndex[childIfdPath]
if found != true {
log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
}
@@ -850,12 +985,12 @@ func (ifd *Ifd) printTagTree(populateValues bool, index, level int, nextLink boo
}
}
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
+ if len(ifd.children) != ifdsFoundCount {
+ log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
}
- if ifd.NextIfd != nil {
- ifd.NextIfd.printTagTree(populateValues, index+1, level, true)
+ if ifd.nextIfd != nil {
+ ifd.nextIfd.printTagTree(populateValues, index+1, level, true)
}
}
@@ -878,12 +1013,12 @@ func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
ifdsFoundCount := 0
- for _, ite := range ifd.Entries {
+ for _, ite := range ifd.entries {
childIfdPath := ite.ChildIfdPath()
if childIfdPath != "" {
ifdsFoundCount++
- childIfd, found := ifd.ChildIfdIndex[childIfdPath]
+ childIfd, found := ifd.childIfdIndex[childIfdPath]
if found != true {
log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
}
@@ -892,12 +1027,12 @@ func (ifd *Ifd) printIfdTree(level int, nextLink bool) {
}
}
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
+ if len(ifd.children) != ifdsFoundCount {
+ log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
}
- if ifd.NextIfd != nil {
- ifd.NextIfd.printIfdTree(level, true)
+ if ifd.nextIfd != nil {
+ ifd.nextIfd.printIfdTree(level, true)
}
}
@@ -914,8 +1049,8 @@ func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string {
indent := strings.Repeat(" ", level*2)
var ifdPhrase string
- if ifd.ParentIfd != nil {
- ifdPhrase = fmt.Sprintf("[%s]->[%s]:(%d)", ifd.ParentIfd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index())
+ if ifd.parentIfd != nil {
+ ifdPhrase = fmt.Sprintf("[%s]->[%s]:(%d)", ifd.parentIfd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index())
} else {
ifdPhrase = fmt.Sprintf("[ROOT]->[%s]:(%d)", ifd.ifdIdentity.UnindexedString(), ifd.ifdIdentity.Index())
}
@@ -924,14 +1059,14 @@ func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string {
tagsDump = append(tagsDump, startBlurb)
ifdsFoundCount := 0
- for _, ite := range ifd.Entries {
+ for _, ite := range ifd.entries {
tagsDump = append(tagsDump, fmt.Sprintf("%s - (0x%04x)", indent, ite.TagId()))
childIfdPath := ite.ChildIfdPath()
if childIfdPath != "" {
ifdsFoundCount++
- childIfd, found := ifd.ChildIfdIndex[childIfdPath]
+ childIfd, found := ifd.childIfdIndex[childIfdPath]
if found != true {
log.Panicf("alien child IFD referenced by a tag: [%s]", childIfdPath)
}
@@ -940,18 +1075,18 @@ func (ifd *Ifd) dumpTree(tagsDump []string, level int) []string {
}
}
- if len(ifd.Children) != ifdsFoundCount {
- log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.Children), ifdsFoundCount)
+ if len(ifd.children) != ifdsFoundCount {
+ log.Panicf("have one or more dangling child IFDs: (%d) != (%d)", len(ifd.children), ifdsFoundCount)
}
finishBlurb := fmt.Sprintf("%s< IFD %s BOTTOM", indent, ifdPhrase)
tagsDump = append(tagsDump, finishBlurb)
- if ifd.NextIfd != nil {
- siblingBlurb := fmt.Sprintf("%s* LINKING TO SIBLING IFD [%s]:(%d)", indent, ifd.NextIfd.ifdIdentity.UnindexedString(), ifd.NextIfd.ifdIdentity.Index())
+ if ifd.nextIfd != nil {
+ siblingBlurb := fmt.Sprintf("%s* LINKING TO SIBLING IFD [%s]:(%d)", indent, ifd.nextIfd.ifdIdentity.UnindexedString(), ifd.nextIfd.ifdIdentity.Index())
tagsDump = append(tagsDump, siblingBlurb)
- tagsDump = ifd.NextIfd.dumpTree(tagsDump, level)
+ tagsDump = ifd.nextIfd.dumpTree(tagsDump, level)
}
return tagsDump
@@ -973,11 +1108,11 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
gi = new(GpsInfo)
- if ifd.ifdIdentity.UnindexedString() != exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString() {
- log.Panicf("GPS can only be read on GPS IFD: [%s] != [%s]", ifd.ifdIdentity.UnindexedString(), exifcommon.IfdGpsInfoStandardIfdIdentity.UnindexedString())
+ if ifd.ifdIdentity.Equals(exifcommon.IfdGpsInfoStandardIfdIdentity) == false {
+ log.Panicf("GPS can only be read on GPS IFD: [%s]", ifd.ifdIdentity.UnindexedString())
}
- if tags, found := ifd.EntriesByTagId[TagGpsVersionId]; found == false {
+ if tags, found := ifd.entriesByTagId[TagGpsVersionId]; found == false {
// We've seen this. We'll just have to default to assuming we're in a
// 2.2.0.0 format.
ifdEnumerateLogger.Warningf(nil, "No GPS version tag (0x%04x) found.", TagGpsVersionId)
@@ -999,7 +1134,7 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
}
}
- tags, found := ifd.EntriesByTagId[TagLatitudeId]
+ tags, found := ifd.entriesByTagId[TagLatitudeId]
if found == false {
ifdEnumerateLogger.Warningf(nil, "latitude not found")
log.Panic(ErrNoGpsTags)
@@ -1009,7 +1144,7 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
log.PanicIf(err)
// Look for whether North or South.
- tags, found = ifd.EntriesByTagId[TagLatitudeRefId]
+ tags, found = ifd.entriesByTagId[TagLatitudeRefId]
if found == false {
ifdEnumerateLogger.Warningf(nil, "latitude-ref not found")
log.Panic(ErrNoGpsTags)
@@ -1018,7 +1153,7 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
latitudeRefValue, err := tags[0].Value()
log.PanicIf(err)
- tags, found = ifd.EntriesByTagId[TagLongitudeId]
+ tags, found = ifd.entriesByTagId[TagLongitudeId]
if found == false {
ifdEnumerateLogger.Warningf(nil, "longitude not found")
log.Panic(ErrNoGpsTags)
@@ -1028,7 +1163,7 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
log.PanicIf(err)
// Look for whether West or East.
- tags, found = ifd.EntriesByTagId[TagLongitudeRefId]
+ tags, found = ifd.entriesByTagId[TagLongitudeRefId]
if found == false {
ifdEnumerateLogger.Warningf(nil, "longitude-ref not found")
log.Panic(ErrNoGpsTags)
@@ -1051,8 +1186,8 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
// Parse altitude.
- altitudeTags, foundAltitude := ifd.EntriesByTagId[TagAltitudeId]
- altitudeRefTags, foundAltitudeRef := ifd.EntriesByTagId[TagAltitudeRefId]
+ altitudeTags, foundAltitude := ifd.entriesByTagId[TagAltitudeId]
+ altitudeRefTags, foundAltitudeRef := ifd.entriesByTagId[TagAltitudeRefId]
if foundAltitude == true && foundAltitudeRef == true {
altitudePhrase, err := altitudeTags[0].Format()
@@ -1085,8 +1220,8 @@ func (ifd *Ifd) GpsInfo() (gi *GpsInfo, err error) {
// Parse timestamp from separate date and time tags.
- timestampTags, foundTimestamp := ifd.EntriesByTagId[TagTimestampId]
- datestampTags, foundDatestamp := ifd.EntriesByTagId[TagDatestampId]
+ timestampTags, foundTimestamp := ifd.entriesByTagId[TagTimestampId]
+ datestampTags, foundDatestamp := ifd.entriesByTagId[TagDatestampId]
if foundTimestamp == true && foundDatestamp == true {
datestampValue, err := datestampTags[0].Value()
@@ -1139,11 +1274,11 @@ func (ifd *Ifd) EnumerateTagsRecursively(visitor ParsedTagVisitor) (err error) {
}
}()
- for ptr := ifd; ptr != nil; ptr = ptr.NextIfd {
- for _, ite := range ifd.Entries {
+ for ptr := ifd; ptr != nil; ptr = ptr.nextIfd {
+ for _, ite := range ifd.entries {
childIfdPath := ite.ChildIfdPath()
if childIfdPath != "" {
- childIfd := ifd.ChildIfdIndex[childIfdPath]
+ childIfd := ifd.childIfdIndex[childIfdPath]
err := childIfd.EnumerateTagsRecursively(visitor)
log.PanicIf(err)
@@ -1254,21 +1389,21 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
ifd := &Ifd{
ifdIdentity: ii,
- ByteOrder: ie.byteOrder,
+ byteOrder: ie.byteOrder,
- Id: id,
+ id: id,
- ParentIfd: parentIfd,
- ParentTagIndex: qi.ParentTagIndex,
+ parentIfd: parentIfd,
+ parentTagIndex: qi.ParentTagIndex,
- Offset: offset,
- Entries: entries,
- EntriesByTagId: entriesByTagId,
+ offset: offset,
+ entries: entries,
+ entriesByTagId: entriesByTagId,
// This is populated as each child is processed.
- Children: make([]*Ifd, 0),
+ children: make([]*Ifd, 0),
- NextIfdOffset: nextIfdOffset,
+ nextIfdOffset: nextIfdOffset,
thumbnailData: thumbnailData,
ifdMapping: ie.ifdMapping,
@@ -1286,13 +1421,13 @@ func (ie *IfdEnumerate) Collect(rootIfdOffset uint32) (index IfdIndex, err error
// Add a link from the previous IFD in the chain to us.
if previousIfd, found := edges[offset]; found == true {
- previousIfd.NextIfd = ifd
+ previousIfd.nextIfd = ifd
}
// Attach as a child to our parent (where we appeared as a tag in
// that IFD).
if parentIfd != nil {
- parentIfd.Children = append(parentIfd.Children, ifd)
+ parentIfd.children = append(parentIfd.children, ifd)
}
// Determine if any of our entries is a child IFD and queue it.
@@ -1362,13 +1497,13 @@ func (ie *IfdEnumerate) setChildrenIndex(ifd *Ifd) (err error) {
}()
childIfdIndex := make(map[string]*Ifd)
- for _, childIfd := range ifd.Children {
+ for _, childIfd := range ifd.children {
childIfdIndex[childIfd.ifdIdentity.UnindexedString()] = childIfd
}
- ifd.ChildIfdIndex = childIfdIndex
+ ifd.childIfdIndex = childIfdIndex
- for _, childIfd := range ifd.Children {
+ for _, childIfd := range ifd.children {
err := ie.setChildrenIndex(childIfd)
log.PanicIf(err)
}
@@ -1391,21 +1526,26 @@ func (ie *IfdEnumerate) FurthestOffset() uint32 {
return ie.furthestOffset
}
-// ParseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for
+// parseOneIfd is a hack to use an IE to parse a raw IFD block. Can be used for
// testing. The fqIfdPath ("fully-qualified IFD path") will be less qualified
// in that the numeric index will always be zero (the zeroth child) rather than
// the proper number (if its actually a sibling to the first child, for
// instance).
-func ParseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor TagVisitorFn) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) {
+func parseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, ifdBlock []byte, visitor TagVisitorFn) (nextIfdOffset uint32, entries []*IfdTagEntry, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
- ie := NewIfdEnumerate(ifdMapping, tagIndex, make([]byte, 0), byteOrder)
+ // TODO(dustin): Add test
+
+ ebs := NewExifReadSeekerWithBytes(ifdBlock)
+
+ rs, err := ebs.GetReadSeeker(0)
+ log.PanicIf(err)
- bp, err := newByteParser(ifdBlock, byteOrder, 0)
+ bp, err := newByteParser(rs, byteOrder, 0)
if err != nil {
if err == ErrOffsetInvalid {
return 0, nil, err
@@ -1414,23 +1554,31 @@ func ParseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exif
log.Panic(err)
}
+ dummyEbs := NewExifReadSeekerWithBytes([]byte{})
+ ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder)
+
nextIfdOffset, entries, _, err = ie.parseIfd(ii, bp, visitor, true, nil)
log.PanicIf(err)
return nextIfdOffset, entries, nil
}
-// ParseOneTag is a hack to use an IE to parse a raw tag block.
-func ParseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, tagBlock []byte) (ite *IfdTagEntry, err error) {
+// parseOneTag is a hack to use an IE to parse a raw tag block.
+func parseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder, tagBlock []byte) (ite *IfdTagEntry, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
- ie := NewIfdEnumerate(ifdMapping, tagIndex, make([]byte, 0), byteOrder)
+ // TODO(dustin): Add test
+
+ ebs := NewExifReadSeekerWithBytes(tagBlock)
- bp, err := newByteParser(tagBlock, byteOrder, 0)
+ rs, err := ebs.GetReadSeeker(0)
+ log.PanicIf(err)
+
+ bp, err := newByteParser(rs, byteOrder, 0)
if err != nil {
if err == ErrOffsetInvalid {
return nil, err
@@ -1439,10 +1587,13 @@ func ParseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exif
log.Panic(err)
}
+ dummyEbs := NewExifReadSeekerWithBytes([]byte{})
+ ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder)
+
ite, err = ie.parseTag(ii, 0, bp)
log.PanicIf(err)
- err = ie.postparseTag(ite, nil)
+ err = ie.tagPostParse(ite, nil)
if err != nil {
if err == ErrTagNotFound {
return nil, err
@@ -1484,16 +1635,16 @@ func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error) {
// TODO(dustin): !! <-- However, we're not sure whether we shouldn't store a secondary IFD-path with the indices. Some IFDs may not necessarily restrict which IFD indices they can be a child of (only the IFD itself matters). Validation should be delegated to the caller.
thisIfd := rootIfd
for currentRootIndex := 0; currentRootIndex < desiredRootIndex; currentRootIndex++ {
- if thisIfd.NextIfd == nil {
+ if thisIfd.nextIfd == nil {
log.Panicf("Root-IFD index (%d) does not exist in the data.", currentRootIndex)
}
- thisIfd = thisIfd.NextIfd
+ thisIfd = thisIfd.nextIfd
}
for _, itii := range lineage {
var hit *Ifd
- for _, childIfd := range thisIfd.Children {
+ for _, childIfd := range thisIfd.children {
if childIfd.ifdIdentity.TagId() == itii.TagId {
hit = childIfd
break
@@ -1502,18 +1653,18 @@ func FindIfdFromRootIfd(rootIfd *Ifd, ifdPath string) (ifd *Ifd, err error) {
// If we didn't find the child, add it.
if hit == nil {
- log.Panicf("IFD [%s] in [%s] not found: %s", itii.Name, ifdPath, thisIfd.Children)
+ log.Panicf("IFD [%s] in [%s] not found: %s", itii.Name, ifdPath, thisIfd.children)
}
thisIfd = hit
// If we didn't find the sibling, add it.
for i := 0; i < itii.Index; i++ {
- if thisIfd.NextIfd == nil {
+ if thisIfd.nextIfd == nil {
log.Panicf("IFD [%s] does not have (%d) occurrences/siblings", thisIfd.ifdIdentity.UnindexedString(), itii.Index)
}
- thisIfd = thisIfd.NextIfd
+ thisIfd = thisIfd.nextIfd
}
}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/ifd_tag_entry.go b/vendor/github.com/dsoprea/go-exif/v3/ifd_tag_entry.go
index 789a9981c..ed6ba2291 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/ifd_tag_entry.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/ifd_tag_entry.go
@@ -2,13 +2,14 @@ package exif
import (
"fmt"
+ "io"
"encoding/binary"
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
- "github.com/dsoprea/go-exif/v2/undefined"
+ "github.com/dsoprea/go-exif/v3/common"
+ "github.com/dsoprea/go-exif/v3/undefined"
)
var (
@@ -42,23 +43,23 @@ type IfdTagEntry struct {
isUnhandledUnknown bool
- addressableData []byte
- byteOrder binary.ByteOrder
+ rs io.ReadSeeker
+ byteOrder binary.ByteOrder
tagName string
}
-func newIfdTagEntry(ii *exifcommon.IfdIdentity, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, addressableData []byte, byteOrder binary.ByteOrder) *IfdTagEntry {
+func newIfdTagEntry(ii *exifcommon.IfdIdentity, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, rs io.ReadSeeker, byteOrder binary.ByteOrder) *IfdTagEntry {
return &IfdTagEntry{
- ifdIdentity: ii,
- tagId: tagId,
- tagIndex: tagIndex,
- tagType: tagType,
- unitCount: unitCount,
- valueOffset: valueOffset,
- rawValueOffset: rawValueOffset,
- addressableData: addressableData,
- byteOrder: byteOrder,
+ ifdIdentity: ii,
+ tagId: tagId,
+ tagIndex: tagIndex,
+ tagType: tagType,
+ unitCount: unitCount,
+ valueOffset: valueOffset,
+ rawValueOffset: rawValueOffset,
+ rs: rs,
+ byteOrder: byteOrder,
}
}
@@ -291,7 +292,7 @@ func (ite *IfdTagEntry) getValueContext() *exifcommon.ValueContext {
ite.unitCount,
ite.valueOffset,
ite.rawValueOffset,
- ite.addressableData,
+ ite.rs,
ite.tagType,
ite.byteOrder)
}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/package.go b/vendor/github.com/dsoprea/go-exif/v3/package.go
index 428f74e3a..428f74e3a 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/package.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/package.go
diff --git a/vendor/github.com/dsoprea/go-exif/v2/tags.go b/vendor/github.com/dsoprea/go-exif/v3/tags.go
index f53d9ce9c..aca902c5d 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/tags.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/tags.go
@@ -2,11 +2,12 @@ package exif
import (
"fmt"
+ "sync"
"github.com/dsoprea/go-logging"
"gopkg.in/yaml.v2"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
const (
@@ -114,7 +115,7 @@ func (it *IndexedTag) Is(ifdPath string, id uint16) bool {
// GetEncodingType returns the largest type that this tag's value can occupy.
func (it *IndexedTag) GetEncodingType(value interface{}) exifcommon.TagTypePrimitive {
// For convenience, we handle encoding a `time.Time` directly.
- if IsTime(value) == true {
+ if exifcommon.IsTime(value) == true {
// Timestamps are encoded as ASCII.
value = ""
}
@@ -177,6 +178,10 @@ func (it *IndexedTag) DoesSupportType(tagType exifcommon.TagTypePrimitive) bool
type TagIndex struct {
tagsByIfd map[string]map[uint16]*IndexedTag
tagsByIfdR map[string]map[string]*IndexedTag
+
+ mutex sync.Mutex
+
+ doUniversalSearch bool
}
// NewTagIndex returns a new TagIndex struct.
@@ -189,6 +194,16 @@ func NewTagIndex() *TagIndex {
return ti
}
+// SetUniversalSearch enables a fallback to matching tags under *any* IFD.
+func (ti *TagIndex) SetUniversalSearch(flag bool) {
+ ti.doUniversalSearch = flag
+}
+
+// UniversalSearch enables a fallback to matching tags under *any* IFD.
+func (ti *TagIndex) UniversalSearch() bool {
+ return ti.doUniversalSearch
+}
+
// Add registers a new tag to be recognized during the parse.
func (ti *TagIndex) Add(it *IndexedTag) (err error) {
defer func() {
@@ -197,6 +212,9 @@ func (ti *TagIndex) Add(it *IndexedTag) (err error) {
}
}()
+ ti.mutex.Lock()
+ defer ti.mutex.Unlock()
+
// Store by ID.
family, found := ti.tagsByIfd[it.IfdPath]
@@ -228,9 +246,7 @@ func (ti *TagIndex) Add(it *IndexedTag) (err error) {
return nil
}
-// Get returns information about the non-IFD tag given a tag ID. `ifdPath` must
-// not be fully-qualified.
-func (ti *TagIndex) Get(ii *exifcommon.IfdIdentity, id uint16) (it *IndexedTag, err error) {
+func (ti *TagIndex) getOne(ifdPath string, id uint16) (it *IndexedTag, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
@@ -242,7 +258,8 @@ func (ti *TagIndex) Get(ii *exifcommon.IfdIdentity, id uint16) (it *IndexedTag,
log.PanicIf(err)
}
- ifdPath := ii.UnindexedString()
+ ti.mutex.Lock()
+ defer ti.mutex.Unlock()
family, found := ti.tagsByIfd[ifdPath]
if found == false {
@@ -257,6 +274,53 @@ func (ti *TagIndex) Get(ii *exifcommon.IfdIdentity, id uint16) (it *IndexedTag,
return it, nil
}
+// Get returns information about the non-IFD tag given a tag ID. `ifdPath` must
+// not be fully-qualified.
+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))
+ }
+ }()
+
+ ifdPath := ii.UnindexedString()
+
+ it, err = ti.getOne(ifdPath, id)
+ if err == nil {
+ return it, nil
+ } else if err != ErrTagNotFound {
+ log.Panic(err)
+ }
+
+ if ti.doUniversalSearch == false {
+ return nil, ErrTagNotFound
+ }
+
+ // We've been told to fallback to look for the tag in other IFDs.
+
+ skipIfdPath := ii.UnindexedString()
+
+ for currentIfdPath, _ := range ti.tagsByIfd {
+ if currentIfdPath == skipIfdPath {
+ // Skip the primary IFD, which has already been checked.
+ continue
+ }
+
+ it, err = ti.getOne(currentIfdPath, id)
+ if err == nil {
+ tagsLogger.Warningf(nil,
+ "Found tag (0x%02x) in the wrong IFD: [%s] != [%s]",
+ id, currentIfdPath, ifdPath)
+
+ return it, nil
+ } else if err != ErrTagNotFound {
+ log.Panic(err)
+ }
+ }
+
+ return nil, ErrTagNotFound
+}
+
var (
// tagGuessDefaultIfdIdentities describes which IFDs we'll look for a given
// tag-ID in, if it's not found where it's supposed to be. We suppose that
diff --git a/vendor/github.com/dsoprea/go-exif/v2/tags_data.go b/vendor/github.com/dsoprea/go-exif/v3/tags_data.go
index a770e5597..dcf0cc4f4 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/tags_data.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/tags_data.go
@@ -59,6 +59,15 @@ IFD/Exif:
- id: 0x9004
name: DateTimeDigitized
type_name: ASCII
+- id: 0x9010
+ name: OffsetTime
+ type_name: ASCII
+- id: 0x9011
+ name: OffsetTimeOriginal
+ type_name: ASCII
+- id: 0x9012
+ name: OffsetTimeDigitized
+ type_name: ASCII
- id: 0x9101
name: ComponentsConfiguration
type_name: UNDEFINED
@@ -909,6 +918,36 @@ IFD:
- id: 0xc74e
name: OpcodeList3
type_name: UNDEFINED
+# This tag may be used to specify the size of raster pixel spacing in the
+# model space units, when the raster space can be embedded in the model space
+# coordinate system without rotation, and consists of the following 3 values:
+# ModelPixelScaleTag = (ScaleX, ScaleY, ScaleZ)
+# where ScaleX and ScaleY give the horizontal and vertical spacing of raster
+# pixels. The ScaleZ is primarily used to map the pixel value of a digital
+# elevation model into the correct Z-scale, and so for most other purposes
+# this value should be zero (since most model spaces are 2-D, with Z=0).
+# Source: http://geotiff.maptools.org/spec/geotiff2.6.html#2.6.1
+- id: 0x830e
+ name: ModelPixelScaleTag
+ type_name: DOUBLE
+# This tag stores raster->model tiepoint pairs in the order
+# ModelTiepointTag = (...,I,J,K, X,Y,Z...),
+# where (I,J,K) is the point at location (I,J) in raster space with
+# pixel-value K, and (X,Y,Z) is a vector in model space. In most cases the
+# model space is only two-dimensional, in which case both K and Z should be
+# set to zero; this third dimension is provided in anticipation of future
+# support for 3D digital elevation models and vertical coordinate systems.
+# Source: http://geotiff.maptools.org/spec/geotiff2.6.html#2.6.1
+- id: 0x8482
+ name: ModelTiepointTag
+ type_name: DOUBLE
+# This tag may be used to specify the transformation matrix between the
+# raster space (and its dependent pixel-value space) and the (possibly 3D)
+# model space.
+# Source: http://geotiff.maptools.org/spec/geotiff2.6.html#2.6.1
+- id: 0x85d8
+ name: ModelTransformationTag
+ type_name: DOUBLE
IFD/Exif/Iop:
- id: 0x0001
name: InteroperabilityIndex
diff --git a/vendor/github.com/dsoprea/go-exif/v2/testing_common.go b/vendor/github.com/dsoprea/go-exif/v3/testing_common.go
index fe69df936..061276430 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/testing_common.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/testing_common.go
@@ -9,7 +9,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
var (
@@ -24,9 +24,9 @@ func getExifSimpleTestIb() *IfdBuilder {
}
}()
- im := NewIfdMapping()
+ im := exifcommon.NewIfdMapping()
- err := LoadStandardIfds(im)
+ err := exifcommon.LoadStandardIfds(im)
log.PanicIf(err)
ti := NewTagIndex()
@@ -55,9 +55,9 @@ func getExifSimpleTestIbBytes() []byte {
}
}()
- im := NewIfdMapping()
+ im := exifcommon.NewIfdMapping()
- err := LoadStandardIfds(im)
+ err := exifcommon.LoadStandardIfds(im)
log.PanicIf(err)
ti := NewTagIndex()
@@ -91,9 +91,9 @@ func validateExifSimpleTestIb(exifData []byte, t *testing.T) {
}
}()
- im := NewIfdMapping()
+ im := exifcommon.NewIfdMapping()
- err := LoadStandardIfds(im)
+ err := exifcommon.LoadStandardIfds(im)
log.PanicIf(err)
ti := NewTagIndex()
@@ -113,19 +113,19 @@ func validateExifSimpleTestIb(exifData []byte, t *testing.T) {
ifd := index.RootIfd
- if ifd.ByteOrder != exifcommon.TestDefaultByteOrder {
+ if ifd.ByteOrder() != exifcommon.TestDefaultByteOrder {
t.Fatalf("IFD byte-order not correct.")
} else if ifd.ifdIdentity.UnindexedString() != exifcommon.IfdStandardIfdIdentity.UnindexedString() {
t.Fatalf("IFD name not correct.")
} else if ifd.ifdIdentity.Index() != 0 {
t.Fatalf("IFD index not zero: (%d)", ifd.ifdIdentity.Index())
- } else if ifd.Offset != uint32(0x0008) {
+ } else if ifd.Offset() != uint32(0x0008) {
t.Fatalf("IFD offset not correct.")
- } else if len(ifd.Entries) != 4 {
- t.Fatalf("IFD number of entries not correct: (%d)", len(ifd.Entries))
- } else if ifd.NextIfdOffset != uint32(0) {
+ } else if len(ifd.Entries()) != 4 {
+ t.Fatalf("IFD number of entries not correct: (%d)", len(ifd.Entries()))
+ } else if ifd.nextIfdOffset != uint32(0) {
t.Fatalf("Next-IFD offset is non-zero.")
- } else if ifd.NextIfd != nil {
+ } else if ifd.nextIfd != nil {
t.Fatalf("Next-IFD pointer is non-nil.")
}
@@ -141,7 +141,7 @@ func validateExifSimpleTestIb(exifData []byte, t *testing.T) {
{tagId: 0x013e, value: []exifcommon.Rational{{Numerator: 0x11112222, Denominator: 0x33334444}}},
}
- for i, ite := range ifd.Entries {
+ for i, ite := range ifd.Entries() {
if ite.TagId() != expected[i].tagId {
t.Fatalf("Tag-ID for entry (%d) not correct: (0x%02x) != (0x%02x)", i, ite.TagId(), expected[i].tagId)
}
@@ -180,3 +180,9 @@ func getTestGpsImageFilepath() string {
testGpsImageFilepath := path.Join(assetsPath, "gps.jpg")
return testGpsImageFilepath
}
+
+func getTestGeotiffFilepath() string {
+ assetsPath := exifcommon.GetTestAssetsPath()
+ testGeotiffFilepath := path.Join(assetsPath, "geotiff_example.tif")
+ return testGeotiffFilepath
+}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/README.md b/vendor/github.com/dsoprea/go-exif/v3/undefined/README.md
index d2caa6e51..d2caa6e51 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/README.md
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/README.md
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/accessor.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/accessor.go
index 3e82c0f61..11a21e1f0 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/accessor.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/accessor.go
@@ -5,7 +5,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
// Encode encodes the given encodeable undefined value to bytes.
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_8828_oecf.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_8828_oecf.go
index 796d17ca7..26f3675ab 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_8828_oecf.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_8828_oecf.go
@@ -8,7 +8,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type Tag8828Oecf struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9000_exif_version.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9000_exif_version.go
index 19cfcc906..8f18c8114 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9000_exif_version.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9000_exif_version.go
@@ -5,7 +5,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type Tag9000ExifVersion struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9101_components_configuration.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9101_components_configuration.go
index 16a4b38e4..e357fe0a6 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9101_components_configuration.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9101_components_configuration.go
@@ -8,7 +8,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
const (
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_927C_maker_note.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_927C_maker_note.go
index e0a52db2a..f9cd2788e 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_927C_maker_note.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_927C_maker_note.go
@@ -9,7 +9,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type Tag927CMakerNote struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9286_user_comment.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9286_user_comment.go
index de07fe19e..320edc145 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_9286_user_comment.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_9286_user_comment.go
@@ -8,7 +8,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
var (
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A000_flashpix_version.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A000_flashpix_version.go
index 28849cde5..4a0fefad7 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A000_flashpix_version.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A000_flashpix_version.go
@@ -5,7 +5,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type TagA000FlashpixVersion struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A20C_spatial_frequency_response.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A20C_spatial_frequency_response.go
index d49c8c52d..0311175d6 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A20C_spatial_frequency_response.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A20C_spatial_frequency_response.go
@@ -8,7 +8,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type TagA20CSpatialFrequencyResponse struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A300_file_source.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A300_file_source.go
index 18a7cdf63..f4f3a49f9 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A300_file_source.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A300_file_source.go
@@ -7,7 +7,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type TagExifA300FileSource uint32
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A301_scene_type.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A301_scene_type.go
index b4246da18..a29fd7673 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A301_scene_type.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A301_scene_type.go
@@ -7,7 +7,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type TagExifA301SceneType uint32
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A302_cfa_pattern.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A302_cfa_pattern.go
index beca78c23..88976296d 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_A302_cfa_pattern.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_A302_cfa_pattern.go
@@ -8,7 +8,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type TagA302CfaPattern struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_iop_0002_interop_version.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_iop_0002_interop_version.go
index eca046b05..09ec98703 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/exif_iop_0002_interop_version.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/exif_iop_0002_interop_version.go
@@ -5,7 +5,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type Tag0002InteropVersion struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/gps_001B_gps_processing_method.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/gps_001B_gps_processing_method.go
index 8583bfb27..6f54d2fc6 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/gps_001B_gps_processing_method.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/gps_001B_gps_processing_method.go
@@ -5,7 +5,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type Tag001BGPSProcessingMethod struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/gps_001C_gps_area_information.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/gps_001C_gps_area_information.go
index 67acceb65..ffdeb905b 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/gps_001C_gps_area_information.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/gps_001C_gps_area_information.go
@@ -5,7 +5,7 @@ import (
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
type Tag001CGPSAreaInformation struct {
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/registration.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/registration.go
index cccc20a82..cccc20a82 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/registration.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/registration.go
diff --git a/vendor/github.com/dsoprea/go-exif/v2/undefined/type.go b/vendor/github.com/dsoprea/go-exif/v3/undefined/type.go
index 29890ef86..ff6ac2b4c 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/undefined/type.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/undefined/type.go
@@ -5,7 +5,7 @@ import (
"encoding/binary"
- "github.com/dsoprea/go-exif/v2/common"
+ "github.com/dsoprea/go-exif/v3/common"
)
const (
diff --git a/vendor/github.com/dsoprea/go-exif/v2/utility.go b/vendor/github.com/dsoprea/go-exif/v3/utility.go
index ad692477e..f0b5e6383 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/utility.go
+++ b/vendor/github.com/dsoprea/go-exif/v3/utility.go
@@ -2,86 +2,20 @@ package exif
import (
"fmt"
+ "io"
"math"
- "reflect"
- "strconv"
- "strings"
- "time"
"github.com/dsoprea/go-logging"
+ "github.com/dsoprea/go-utility/v2/filesystem"
- "github.com/dsoprea/go-exif/v2/common"
- "github.com/dsoprea/go-exif/v2/undefined"
+ "github.com/dsoprea/go-exif/v3/common"
+ "github.com/dsoprea/go-exif/v3/undefined"
)
var (
utilityLogger = log.NewLogger("exif.utility")
)
-var (
- timeType = reflect.TypeOf(time.Time{})
-)
-
-// ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC
-// `time.Time` struct.
-func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- parts := strings.Split(fullTimestampPhrase, " ")
- datestampValue, timestampValue := parts[0], parts[1]
-
- // Normalize the separators.
- datestampValue = strings.ReplaceAll(datestampValue, "-", ":")
- timestampValue = strings.ReplaceAll(timestampValue, "-", ":")
-
- dateParts := strings.Split(datestampValue, ":")
-
- year, err := strconv.ParseUint(dateParts[0], 10, 16)
- if err != nil {
- log.Panicf("could not parse year")
- }
-
- month, err := strconv.ParseUint(dateParts[1], 10, 8)
- if err != nil {
- log.Panicf("could not parse month")
- }
-
- day, err := strconv.ParseUint(dateParts[2], 10, 8)
- if err != nil {
- log.Panicf("could not parse day")
- }
-
- timeParts := strings.Split(timestampValue, ":")
-
- hour, err := strconv.ParseUint(timeParts[0], 10, 8)
- if err != nil {
- log.Panicf("could not parse hour")
- }
-
- minute, err := strconv.ParseUint(timeParts[1], 10, 8)
- if err != nil {
- log.Panicf("could not parse minute")
- }
-
- second, err := strconv.ParseUint(timeParts[2], 10, 8)
- if err != nil {
- log.Panicf("could not parse second")
- }
-
- timestamp = time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.UTC)
- return timestamp, nil
-}
-
-// ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a
-// `time.Time` struct. It will attempt to convert to UTC first.
-func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
- return exifcommon.ExifFullTimestampString(t)
-}
-
// ExifTag is one simple representation of a tag in a flat list of all of them.
type ExifTag struct {
// IfdPath is the fully-qualified IFD path (even though it is not named as
@@ -137,24 +71,93 @@ func (et ExifTag) String() string {
}
// GetFlatExifData returns a simple, flat representation of all tags.
-func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) {
+func GetFlatExifData(exifData []byte, so *ScanOptions) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
- eh, err := ParseExifHeader(exifData)
+ sb := rifs.NewSeekableBufferWithBytes(exifData)
+
+ exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(sb, so, false)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// RELEASE(dustin): GetFlatExifDataUniversalSearch is a kludge to allow univeral tag searching in a backwards-compatible manner. For the next release, undo this and simply add the flag to GetFlatExifData.
+
+// GetFlatExifDataUniversalSearch returns a simple, flat representation of all
+// tags.
+func GetFlatExifDataUniversalSearch(exifData []byte, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ sb := rifs.NewSeekableBufferWithBytes(exifData)
+
+ exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(sb, so, doUniversalSearch)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// RELEASE(dustin): GetFlatExifDataUniversalSearchWithReadSeeker is a kludge to allow using a ReadSeeker in a backwards-compatible manner. For the next release, drop this and refactor GetFlatExifDataUniversalSearch to take a ReadSeeker.
+
+// GetFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat
+// representation of all tags given a ReadSeeker.
+func GetFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ exifTags, med, err = getFlatExifDataUniversalSearchWithReadSeeker(rs, so, doUniversalSearch)
+ log.PanicIf(err)
+
+ return exifTags, med, nil
+}
+
+// getFlatExifDataUniversalSearchWithReadSeeker returns a simple, flat
+// representation of all tags given a ReadSeeker.
+func getFlatExifDataUniversalSearchWithReadSeeker(rs io.ReadSeeker, so *ScanOptions, doUniversalSearch bool) (exifTags []ExifTag, med *MiscellaneousExifData, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ headerData := make([]byte, ExifSignatureLength)
+ if _, err = io.ReadFull(rs, headerData); err != nil {
+ if err == io.EOF {
+ return nil, nil, err
+ }
+
+ log.Panic(err)
+ }
+
+ eh, err := ParseExifHeader(headerData)
+ log.PanicIf(err)
+
+ im, err := exifcommon.NewIfdMappingWithStandard()
log.PanicIf(err)
- im := NewIfdMappingWithStandard()
ti := NewTagIndex()
- ie := NewIfdEnumerate(im, ti, exifData, eh.ByteOrder)
+ if doUniversalSearch == true {
+ ti.SetUniversalSearch(true)
+ }
+
+ ebs := NewExifReadSeeker(rs)
+ ie := NewIfdEnumerate(im, ti, ebs, eh.ByteOrder)
exifTags = make([]ExifTag, 0)
- visitor := func(fqIfdPath string, ifdIndex int, ite *IfdTagEntry) (err error) {
+ visitor := func(ite *IfdTagEntry) (err error) {
// This encodes down to base64. Since this an example tool and we do not
// expect to ever decode the output, we are not worried about
// specifically base64-encoding it in order to have a measure of
@@ -172,13 +175,19 @@ func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) {
if err != nil {
if err == exifcommon.ErrUnhandledUndefinedTypedTag {
value = exifundefined.UnparseableUnknownTagValuePlaceholder
+ } else if log.Is(err, exifcommon.ErrParseFail) == true {
+ utilityLogger.Warningf(nil,
+ "Could not parse value for tag [%s] (%04x) [%s].",
+ ite.IfdPath(), ite.TagId(), ite.TagName())
+
+ return nil
} else {
log.Panic(err)
}
}
et := ExifTag{
- IfdPath: fqIfdPath,
+ IfdPath: ite.IfdPath(),
TagId: ite.TagId(),
TagName: ite.TagName(),
UnitCount: ite.UnitCount(),
@@ -200,10 +209,10 @@ func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) {
return nil
}
- _, err = ie.Scan(exifcommon.IfdStandardIfdIdentity, eh.FirstIfdOffset, visitor)
+ med, err = ie.Scan(exifcommon.IfdStandardIfdIdentity, eh.FirstIfdOffset, visitor, nil)
log.PanicIf(err)
- return exifTags, nil
+ return exifTags, med, nil
}
// GpsDegreesEquals returns true if the two `GpsDegrees` are identical.
@@ -226,8 +235,3 @@ func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool {
return true
}
-
-// IsTime returns true if the value is a `time.Time`.
-func IsTime(v interface{}) bool {
- return reflect.TypeOf(v) == timeType
-}
diff --git a/vendor/github.com/dsoprea/go-exif/value_context.go b/vendor/github.com/dsoprea/go-exif/value_context.go
deleted file mode 100644
index 3fce352a3..000000000
--- a/vendor/github.com/dsoprea/go-exif/value_context.go
+++ /dev/null
@@ -1,367 +0,0 @@
-package exif
-
-import (
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-var (
- parser *Parser
-)
-
-// ValueContext describes all of the parameters required to find and extract
-// the actual tag value.
-type ValueContext struct {
- unitCount uint32
- valueOffset uint32
- rawValueOffset []byte
- addressableData []byte
-
- tagType TagTypePrimitive
- byteOrder binary.ByteOrder
-
- // undefinedValueTagType is the effective type to use if this is an
- // "undefined" value.
- undefinedValueTagType TagTypePrimitive
-
- ifdPath string
- tagId uint16
-}
-
-func newValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
- return &ValueContext{
- unitCount: unitCount,
- valueOffset: valueOffset,
- rawValueOffset: rawValueOffset,
- addressableData: addressableData,
-
- tagType: tagType,
- byteOrder: byteOrder,
-
- ifdPath: ifdPath,
- tagId: tagId,
- }
-}
-
-func newValueContextFromTag(ite *IfdTagEntry, addressableData []byte, byteOrder binary.ByteOrder) *ValueContext {
- return newValueContext(
- ite.IfdPath,
- ite.TagId,
- ite.UnitCount,
- ite.ValueOffset,
- ite.RawValueOffset,
- addressableData,
- ite.TagType,
- byteOrder)
-}
-
-func (vc *ValueContext) SetUnknownValueType(tagType TagTypePrimitive) {
- vc.undefinedValueTagType = tagType
-}
-
-func (vc *ValueContext) UnitCount() uint32 {
- return vc.unitCount
-}
-
-func (vc *ValueContext) ValueOffset() uint32 {
- return vc.valueOffset
-}
-
-func (vc *ValueContext) RawValueOffset() []byte {
- return vc.rawValueOffset
-}
-
-func (vc *ValueContext) AddressableData() []byte {
- return vc.addressableData
-}
-
-// isEmbedded returns whether the value is embedded or a reference. This can't
-// be precalculated since the size is not defined for all types (namely the
-// "undefined" types).
-func (vc *ValueContext) isEmbedded() bool {
- tagType := vc.effectiveValueType()
-
- return (tagType.Size() * int(vc.unitCount)) <= 4
-}
-
-func (vc *ValueContext) effectiveValueType() (tagType TagTypePrimitive) {
- if vc.tagType == TypeUndefined {
- tagType = vc.undefinedValueTagType
-
- if tagType == 0 {
- log.Panicf("undefined-value type not set")
- }
- } else {
- tagType = vc.tagType
- }
-
- return tagType
-}
-
-func (vc *ValueContext) readRawEncoded() (rawBytes []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- tagType := vc.effectiveValueType()
-
- unitSizeRaw := uint32(tagType.Size())
-
- if vc.isEmbedded() == true {
- byteLength := unitSizeRaw * vc.unitCount
- return vc.rawValueOffset[:byteLength], nil
- } else {
- return vc.addressableData[vc.valueOffset : vc.valueOffset+vc.unitCount*unitSizeRaw], nil
- }
-}
-
-// Format returns a string representation for the value.
-//
-// Where the type is not ASCII, `justFirst` indicates whether to just stringify
-// the first item in the slice (or return an empty string if the slice is
-// empty).
-//
-// Since this method lacks the information to process undefined-type tags (e.g.
-// byte-order, tag-ID, IFD type), it will return an error if attempted. See
-// `Undefined()`.
-func (vc *ValueContext) Format() (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawBytes, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- phrase, err := Format(rawBytes, vc.tagType, false, vc.byteOrder)
- log.PanicIf(err)
-
- return phrase, nil
-}
-
-// FormatOne is similar to `Format` but only gets and stringifies the first
-// item.
-func (vc *ValueContext) FormatFirst() (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawBytes, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- phrase, err := Format(rawBytes, vc.tagType, true, vc.byteOrder)
- log.PanicIf(err)
-
- return phrase, nil
-}
-
-func (vc *ValueContext) ReadBytes() (value []byte, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseBytes(rawValue, vc.unitCount)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadAscii() (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseAscii(rawValue, vc.unitCount)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadAsciiNoNul() (value string, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseAsciiNoNul(rawValue, vc.unitCount)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadShorts() (value []uint16, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseShorts(rawValue, vc.unitCount, vc.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadLongs() (value []uint32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseLongs(rawValue, vc.unitCount, vc.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadRationals() (value []Rational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseRationals(rawValue, vc.unitCount, vc.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadSignedLongs() (value []int32, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseSignedLongs(rawValue, vc.unitCount, vc.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-func (vc *ValueContext) ReadSignedRationals() (value []SignedRational, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- rawValue, err := vc.readRawEncoded()
- log.PanicIf(err)
-
- value, err = parser.ParseSignedRationals(rawValue, vc.unitCount, vc.byteOrder)
- log.PanicIf(err)
-
- return value, nil
-}
-
-// Values knows how to resolve the given value. This value is always a list
-// (undefined-values aside), so we're named accordingly.
-//
-// Since this method lacks the information to process unknown-type tags (e.g.
-// byte-order, tag-ID, IFD type), it will return an error if attempted. See
-// `Undefined()`.
-func (vc *ValueContext) Values() (values interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- if vc.tagType == TypeByte {
- values, err = vc.ReadBytes()
- log.PanicIf(err)
- } else if vc.tagType == TypeAscii {
- values, err = vc.ReadAscii()
- log.PanicIf(err)
- } else if vc.tagType == TypeAsciiNoNul {
- values, err = vc.ReadAsciiNoNul()
- log.PanicIf(err)
- } else if vc.tagType == TypeShort {
- values, err = vc.ReadShorts()
- log.PanicIf(err)
- } else if vc.tagType == TypeLong {
- values, err = vc.ReadLongs()
- log.PanicIf(err)
- } else if vc.tagType == TypeRational {
- values, err = vc.ReadRationals()
- log.PanicIf(err)
- } else if vc.tagType == TypeSignedLong {
- values, err = vc.ReadSignedLongs()
- log.PanicIf(err)
- } else if vc.tagType == TypeSignedRational {
- values, err = vc.ReadSignedRationals()
- log.PanicIf(err)
- } else if vc.tagType == TypeUndefined {
- log.Panicf("will not parse undefined-type value")
-
- // Never called.
- return nil, nil
- } else {
- log.Panicf("value of type [%s] is unparseable", vc.tagType)
-
- // Never called.
- return nil, nil
- }
-
- return values, nil
-}
-
-// Undefined attempts to identify and decode supported undefined-type fields.
-// This is the primary, preferred interface to reading undefined values.
-func (vc *ValueContext) Undefined() (value interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- value, err = UndefinedValue(vc.ifdPath, vc.tagId, vc, vc.byteOrder)
- if err != nil {
- if err == ErrUnhandledUnknownTypedTag {
- return nil, err
- }
-
- log.Panic(err)
- }
-
- return value, nil
-}
-
-func init() {
- parser = &Parser{}
-}
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/.travis.yml b/vendor/github.com/dsoprea/go-jpeg-image-structure/.travis.yml
deleted file mode 100644
index 4c79875ed..000000000
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: go
-go:
- - master
- - stable
- - "1.14"
- - "1.13"
- - "1.12"
-env:
- - GO111MODULE=on
-install:
- - go get -t ./...
-script:
-# v1
- - go test -v .
-# v2
- - cd v2
- - go test -v ./... -coverprofile=coverage.txt -covermode=atomic
- - cd ..
-after_success:
- - cd v2
- - curl -s https://codecov.io/bash | bash
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/.MODULE_ROOT b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/.MODULE_ROOT
index e69de29bb..e69de29bb 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/.MODULE_ROOT
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/.MODULE_ROOT
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/LICENSE b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/LICENSE
index 163291ed6..163291ed6 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/LICENSE
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/LICENSE
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/README.md b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/README.md
index 67bc57617..bf60ef504 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/README.md
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/README.md
@@ -1,5 +1,5 @@
-[![Build Status](https://travis-ci.org/dsoprea/go-jpeg-image-structure.svg?branch=master)](https://travis-ci.org/dsoprea/go-jpeg-image-structure)
-[![codecov](https://codecov.io/gh/dsoprea/go-jpeg-image-structure/branch/master/graph/badge.svg?token=Twxyx7kpAa)](https://codecov.io/gh/dsoprea/go-jpeg-image-structure)
+[![Build Status](https://travis-ci.org/dsoprea/go-jpeg-image-structure/v2.svg?branch=master)](https://travis-ci.org/dsoprea/go-jpeg-image-structure/v2)
+[![codecov](https://codecov.io/gh/dsoprea/go-jpeg-image-structure/branch/master/graph/badge.svg)](https://codecov.io/gh/dsoprea/go-jpeg-image-structure)
[![Go Report Card](https://goreportcard.com/badge/github.com/dsoprea/go-jpeg-image-structure/v2)](https://goreportcard.com/report/github.com/dsoprea/go-jpeg-image-structure/v2)
[![GoDoc](https://godoc.org/github.com/dsoprea/go-jpeg-image-structure/v2?status.svg)](https://godoc.org/github.com/dsoprea/go-jpeg-image-structure/v2)
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/markers.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/markers.go
index a12171bd8..a12171bd8 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/markers.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/markers.go
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/media_parser.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/media_parser.go
index dd4c73af9..e6fc60bc4 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/media_parser.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/media_parser.go
@@ -3,11 +3,14 @@ package jpegstructure
import (
"bufio"
"bytes"
+ "image"
"io"
"os"
+ "image/jpeg"
+
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-utility/image"
+ "github.com/dsoprea/go-utility/v2/image"
)
// JpegMediaParser is a `riimage.MediaParser` that knows how to parse JPEG
@@ -122,6 +125,14 @@ func (jmp *JpegMediaParser) LooksLikeFormat(data []byte) bool {
return true
}
+// GetImage returns an image.Image-compatible struct.
+func (jmp *JpegMediaParser) GetImage(r io.Reader) (img image.Image, err error) {
+ img, err = jpeg.Decode(r)
+ log.PanicIf(err)
+
+ return img, nil
+}
+
var (
// Enforce interface conformance.
_ riimage.MediaParser = new(JpegMediaParser)
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/segment.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/segment.go
index d6a1c42bb..6b433bf1f 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/segment.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/segment.go
@@ -8,11 +8,12 @@ import (
"crypto/sha1"
"encoding/hex"
- "github.com/dsoprea/go-exif/v2"
+ "github.com/dsoprea/go-exif/v3"
+ "github.com/dsoprea/go-exif/v3/common"
"github.com/dsoprea/go-iptc"
"github.com/dsoprea/go-logging"
"github.com/dsoprea/go-photoshop-info-format"
- "github.com/dsoprea/go-utility/image"
+ "github.com/dsoprea/go-utility/v2/image"
)
const (
@@ -125,7 +126,9 @@ func (s *Segment) Exif() (rootIfd *exif.Ifd, data []byte, err error) {
jpegLogger.Debugf(nil, "Attempting to parse (%d) byte EXIF blob (Exif).", len(rawExif))
- im := exif.NewIfdMappingWithStandard()
+ im, err := exifcommon.NewIfdMappingWithStandard()
+ log.PanicIf(err)
+
ti := exif.NewTagIndex()
_, index, err := exif.Collect(im, ti, rawExif)
@@ -150,7 +153,7 @@ func (s *Segment) FlatExif() (exifTags []exif.ExifTag, err error) {
jpegLogger.Debugf(nil, "Attempting to parse (%d) byte EXIF blob (FlatExif).", len(rawExif))
- exifTags, err = exif.GetFlatExifData(rawExif)
+ exifTags, _, err = exif.GetFlatExifData(rawExif, nil)
log.PanicIf(err)
return exifTags, nil
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/segment_list.go
index 1b5b06a3a..b4f4d5810 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/segment_list.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/segment_list.go
@@ -8,7 +8,8 @@ import (
"crypto/sha1"
"encoding/binary"
- "github.com/dsoprea/go-exif/v2"
+ "github.com/dsoprea/go-exif/v3"
+ "github.com/dsoprea/go-exif/v3/common"
"github.com/dsoprea/go-iptc"
"github.com/dsoprea/go-logging"
)
@@ -241,11 +242,31 @@ func (sl *SegmentList) ConstructExifBuilder() (rootIb *exif.IfdBuilder, err erro
}()
rootIfd, _, err := sl.Exif()
- log.PanicIf(err)
+ if log.Is(err, exif.ErrNoExif) == true {
+ // No EXIF. Just create a boilerplate builder.
+
+ im := exifcommon.NewIfdMapping()
+
+ err := exifcommon.LoadStandardIfds(im)
+ log.PanicIf(err)
+
+ ti := exif.NewTagIndex()
+
+ rootIb :=
+ exif.NewIfdBuilder(
+ im,
+ ti,
+ exifcommon.IfdStandardIfdIdentity,
+ exifcommon.EncodeDefaultByteOrder)
+
+ return rootIb, nil
+ } else if err != nil {
+ log.Panic(err)
+ }
- ib := exif.NewIfdBuilderFromExistingChain(rootIfd)
+ rootIb = exif.NewIfdBuilderFromExistingChain(rootIfd)
- return ib, nil
+ return rootIb, nil
}
// DumpExif returns an unstructured list of tags (useful when just reviewing).
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/splitter.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/splitter.go
index 8e9c7c020..8e9c7c020 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/splitter.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/splitter.go
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/testing_common.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/testing_common.go
index e7169c2f0..e7169c2f0 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/testing_common.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/testing_common.go
diff --git a/vendor/github.com/dsoprea/go-jpeg-image-structure/utility.go b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/utility.go
index 1c618ba6d..1c618ba6d 100644
--- a/vendor/github.com/dsoprea/go-jpeg-image-structure/utility.go
+++ b/vendor/github.com/dsoprea/go-jpeg-image-structure/v2/utility.go
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/.travis.yml b/vendor/github.com/dsoprea/go-png-image-structure/.travis.yml
deleted file mode 100644
index fdeab54e1..000000000
--- a/vendor/github.com/dsoprea/go-png-image-structure/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: go
-go:
- - master
- - stable
- - "1.14"
- - "1.13"
- - "1.12"
-env:
- - GO111MODULE=on
-install:
- - go get -t ./...
-script:
-# v1
- - go test -v .
-# v2
- - cd v2
- - go test -v .
- - cd ..
-after_success:
- - cd v2
- - curl -s https://codecov.io/bash | bash
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/README.md b/vendor/github.com/dsoprea/go-png-image-structure/README.md
deleted file mode 100644
index 46297a2a7..000000000
--- a/vendor/github.com/dsoprea/go-png-image-structure/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-[![Build Status](https://travis-ci.org/dsoprea/go-png-image-structure.svg?branch=master)](https://travis-ci.org/dsoprea/go-png-image-structure)
-[![codecov](https://codecov.io/gh/dsoprea/go-png-image-structure/branch/master/graph/badge.svg)](https://codecov.io/gh/dsoprea/go-png-image-structure)
-[![Go Report Card](https://goreportcard.com/badge/github.com/dsoprea/go-png-image-structure/v2)](https://goreportcard.com/report/github.com/dsoprea/go-png-image-structure/v2)
-[![GoDoc](https://godoc.org/github.com/dsoprea/go-png-image-structure/v2?status.svg)](https://godoc.org/github.com/dsoprea/go-png-image-structure/v2)
-
-## Overview
-
-Parse raw PNG data into individual chunks. Parse/modify EXIF data and write an updated image.
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/chunk_decoder.go b/vendor/github.com/dsoprea/go-png-image-structure/chunk_decoder.go
deleted file mode 100644
index 1358c3df2..000000000
--- a/vendor/github.com/dsoprea/go-png-image-structure/chunk_decoder.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package pngstructure
-
-import (
- "fmt"
- "bytes"
-
- "encoding/binary"
-
- "github.com/dsoprea/go-logging"
-)
-
-type ChunkDecoder struct {
-
-}
-
-func NewChunkDecoder() *ChunkDecoder {
- return new(ChunkDecoder)
-}
-
-func (cd *ChunkDecoder) Decode(c *Chunk) (decoded interface{}, err error) {
- defer func() {
- if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
- }
- }()
-
- switch c.Type {
- case "IHDR":
- ihdr, err := cd.decodeIHDR(c)
- log.PanicIf(err)
-
- return ihdr, nil
- }
-
- // We don't decode this particular type.
- return nil, nil
-}
-
-
-type ChunkIHDR struct {
- Width uint32
- Height uint32
- BitDepth uint8
- ColorType uint8
- CompressionMethod uint8
- FilterMethod uint8
- InterlaceMethod uint8
-}
-
-func (ihdr *ChunkIHDR) String() string {
- return fmt.Sprintf("IHDR<WIDTH=(%d) HEIGHT=(%d) DEPTH=(%d) COLOR-TYPE=(%d) COMP-METHOD=(%d) FILTER-METHOD=(%d) INTRLC-METHOD=(%d)>", ihdr.Width, ihdr.Height, ihdr.BitDepth, ihdr.ColorType, ihdr.CompressionMethod, ihdr.FilterMethod, ihdr.InterlaceMethod)
-}
-
-func (cd *ChunkDecoder) decodeIHDR(c *Chunk) (ihdr *ChunkIHDR, err error) {
- defer func() {
- if state := recover(); state != nil {
- err := log.Wrap(state.(error))
- log.Panic(err)
- }
- }()
-
- b := bytes.NewBuffer(c.Data)
-
- ihdr = new(ChunkIHDR)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.Width)
- log.PanicIf(err)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.Height)
- log.PanicIf(err)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.BitDepth)
- log.PanicIf(err)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.ColorType)
- log.PanicIf(err)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.CompressionMethod)
- log.PanicIf(err)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.FilterMethod)
- log.PanicIf(err)
-
- err = binary.Read(b, binary.BigEndian, &ihdr.InterlaceMethod)
- log.PanicIf(err)
-
- return ihdr, nil
-}
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/media_parser.go b/vendor/github.com/dsoprea/go-png-image-structure/media_parser.go
deleted file mode 100644
index f7467593f..000000000
--- a/vendor/github.com/dsoprea/go-png-image-structure/media_parser.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package pngstructure
-
-import (
- "bufio"
- "bytes"
- "io"
- "os"
-
- "github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-utility/image"
-)
-
-// PngMediaParser knows how to parse a PNG stream.
-type PngMediaParser struct {
-}
-
-// NewPngMediaParser returns a new `PngMediaParser` struct.
-func NewPngMediaParser() *PngMediaParser {
-
- // TODO(dustin): Add test
-
- return new(PngMediaParser)
-}
-
-// Parse parses a PNG stream given a `io.ReadSeeker`.
-func (pmp *PngMediaParser) Parse(rs io.ReadSeeker, size int) (mc riimage.MediaContext, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): Add test
-
- ps := NewPngSplitter()
-
- err = ps.readHeader(rs)
- log.PanicIf(err)
-
- s := bufio.NewScanner(rs)
-
- // Since each segment can be any size, our buffer must be allowed to grow
- // as large as the file.
- buffer := []byte{}
- s.Buffer(buffer, size)
- s.Split(ps.Split)
-
- for s.Scan() != false {
- }
- log.PanicIf(s.Err())
-
- return ps.Chunks(), nil
-}
-
-// ParseFile parses a PNG stream given a file-path.
-func (pmp *PngMediaParser) ParseFile(filepath string) (mc riimage.MediaContext, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- f, err := os.Open(filepath)
- log.PanicIf(err)
-
- defer f.Close()
-
- stat, err := f.Stat()
- log.PanicIf(err)
-
- size := stat.Size()
-
- chunks, err := pmp.Parse(f, int(size))
- log.PanicIf(err)
-
- return chunks, nil
-}
-
-// ParseBytes parses a PNG stream given a byte-slice.
-func (pmp *PngMediaParser) ParseBytes(data []byte) (mc riimage.MediaContext, err error) {
- defer func() {
- if state := recover(); state != nil {
- err = log.Wrap(state.(error))
- }
- }()
-
- // TODO(dustin): Add test
-
- br := bytes.NewReader(data)
-
- chunks, err := pmp.Parse(br, len(data))
- log.PanicIf(err)
-
- return chunks, nil
-}
-
-// LooksLikeFormat returns a boolean indicating whether the stream looks like a
-// PNG image.
-func (pmp *PngMediaParser) LooksLikeFormat(data []byte) bool {
- return bytes.Compare(data[:len(PngSignature)], PngSignature[:]) == 0
-}
-
-var (
- // Enforce interface conformance.
- _ riimage.MediaParser = new(PngMediaParser)
-)
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/testing_common.go b/vendor/github.com/dsoprea/go-png-image-structure/testing_common.go
deleted file mode 100644
index e7dad11af..000000000
--- a/vendor/github.com/dsoprea/go-png-image-structure/testing_common.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package pngstructure
-
-import (
- "os"
- "path"
-
- "github.com/dsoprea/go-logging"
-)
-
-var (
- assetsPath = ""
-)
-
-func getModuleRootPath() string {
- moduleRootPath := os.Getenv("PNG_MODULE_ROOT_PATH")
- if moduleRootPath != "" {
- return moduleRootPath
- }
-
- currentWd, err := os.Getwd()
- log.PanicIf(err)
-
- currentPath := currentWd
- visited := make([]string, 0)
-
- for {
- tryStampFilepath := path.Join(currentPath, ".MODULE_ROOT")
-
- _, err := os.Stat(tryStampFilepath)
- if err != nil && os.IsNotExist(err) != true {
- log.Panic(err)
- } else if err == nil {
- break
- }
-
- visited = append(visited, tryStampFilepath)
-
- currentPath = path.Dir(currentPath)
- if currentPath == "/" {
- log.Panicf("could not find module-root: %v", visited)
- }
- }
-
- return currentPath
-}
-
-func getTestAssetsPath() string {
- if assetsPath == "" {
- moduleRootPath := getModuleRootPath()
- assetsPath = path.Join(moduleRootPath, "assets")
- }
-
- return assetsPath
-}
-
-func getTestBasicImageFilepath() string {
- assetsPath := getTestAssetsPath()
- return path.Join(assetsPath, "libpng.png")
-}
-
-func getTestExifImageFilepath() string {
- assetsPath := getTestAssetsPath()
- return path.Join(assetsPath, "exif.png")
-}
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/utility.go b/vendor/github.com/dsoprea/go-png-image-structure/utility.go
deleted file mode 100644
index 9bfab14a4..000000000
--- a/vendor/github.com/dsoprea/go-png-image-structure/utility.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package pngstructure
-
-import (
- "fmt"
- "bytes"
-
- "github.com/dsoprea/go-logging"
-)
-
-func DumpBytes(data []byte) {
- fmt.Printf("DUMP: ")
- for _, x := range data {
- fmt.Printf("%02x ", x)
- }
-
- fmt.Printf("\n")
-}
-
-func DumpBytesClause(data []byte) {
- fmt.Printf("DUMP: ")
-
- fmt.Printf("[]byte { ")
-
- for i, x := range data {
- fmt.Printf("0x%02x", x)
-
- if i < len(data) - 1 {
- fmt.Printf(", ")
- }
- }
-
- fmt.Printf(" }\n")
-}
-
-func DumpBytesToString(data []byte) string {
- b := new(bytes.Buffer)
-
- for i, x := range data {
- _, err := b.WriteString(fmt.Sprintf("%02x", x))
- log.PanicIf(err)
-
- if i < len(data) - 1 {
- _, err := b.WriteRune(' ')
- log.PanicIf(err)
- }
- }
-
- return b.String()
-}
-
-func DumpBytesClauseToString(data []byte) string {
- b := new(bytes.Buffer)
-
- for i, x := range data {
- _, err := b.WriteString(fmt.Sprintf("0x%02x", x))
- log.PanicIf(err)
-
- if i < len(data) - 1 {
- _, err := b.WriteString(", ")
- log.PanicIf(err)
- }
- }
-
- return b.String()
-}
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/.MODULE_ROOT b/vendor/github.com/dsoprea/go-png-image-structure/v2/.MODULE_ROOT
index e69de29bb..e69de29bb 100644
--- a/vendor/github.com/dsoprea/go-png-image-structure/.MODULE_ROOT
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/.MODULE_ROOT
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/LICENSE b/vendor/github.com/dsoprea/go-png-image-structure/v2/LICENSE
index 163291ed6..163291ed6 100644
--- a/vendor/github.com/dsoprea/go-png-image-structure/LICENSE
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/LICENSE
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/v2/chunk_decoder.go b/vendor/github.com/dsoprea/go-png-image-structure/v2/chunk_decoder.go
new file mode 100644
index 000000000..b5e0b1b16
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/chunk_decoder.go
@@ -0,0 +1,87 @@
+package pngstructure
+
+import (
+ "bytes"
+ "fmt"
+
+ "encoding/binary"
+
+ "github.com/dsoprea/go-logging"
+)
+
+type ChunkDecoder struct {
+}
+
+func NewChunkDecoder() *ChunkDecoder {
+ return new(ChunkDecoder)
+}
+
+func (cd *ChunkDecoder) Decode(c *Chunk) (decoded interface{}, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err := log.Wrap(state.(error))
+ log.Panic(err)
+ }
+ }()
+
+ switch c.Type {
+ case "IHDR":
+ ihdr, err := cd.decodeIHDR(c)
+ log.PanicIf(err)
+
+ return ihdr, nil
+ }
+
+ // We don't decode this particular type.
+ return nil, nil
+}
+
+type ChunkIHDR struct {
+ Width uint32
+ Height uint32
+ BitDepth uint8
+ ColorType uint8
+ CompressionMethod uint8
+ FilterMethod uint8
+ InterlaceMethod uint8
+}
+
+func (ihdr *ChunkIHDR) String() string {
+ return fmt.Sprintf("IHDR<WIDTH=(%d) HEIGHT=(%d) DEPTH=(%d) COLOR-TYPE=(%d) COMP-METHOD=(%d) FILTER-METHOD=(%d) INTRLC-METHOD=(%d)>", ihdr.Width, ihdr.Height, ihdr.BitDepth, ihdr.ColorType, ihdr.CompressionMethod, ihdr.FilterMethod, ihdr.InterlaceMethod)
+}
+
+func (cd *ChunkDecoder) decodeIHDR(c *Chunk) (ihdr *ChunkIHDR, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err := log.Wrap(state.(error))
+ log.Panic(err)
+ }
+ }()
+
+ b := bytes.NewBuffer(c.Data)
+
+ ihdr = new(ChunkIHDR)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.Width)
+ log.PanicIf(err)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.Height)
+ log.PanicIf(err)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.BitDepth)
+ log.PanicIf(err)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.ColorType)
+ log.PanicIf(err)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.CompressionMethod)
+ log.PanicIf(err)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.FilterMethod)
+ log.PanicIf(err)
+
+ err = binary.Read(b, binary.BigEndian, &ihdr.InterlaceMethod)
+ log.PanicIf(err)
+
+ return ihdr, nil
+}
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/v2/media_parser.go b/vendor/github.com/dsoprea/go-png-image-structure/v2/media_parser.go
new file mode 100644
index 000000000..c0e287365
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/media_parser.go
@@ -0,0 +1,118 @@
+package pngstructure
+
+import (
+ "bufio"
+ "bytes"
+ "image"
+ "io"
+ "os"
+
+ "image/png"
+
+ "github.com/dsoprea/go-logging"
+ "github.com/dsoprea/go-utility/v2/image"
+)
+
+// PngMediaParser knows how to parse a PNG stream.
+type PngMediaParser struct {
+}
+
+// NewPngMediaParser returns a new `PngMediaParser` struct.
+func NewPngMediaParser() *PngMediaParser {
+
+ // TODO(dustin): Add test
+
+ return new(PngMediaParser)
+}
+
+// Parse parses a PNG stream given a `io.ReadSeeker`.
+func (pmp *PngMediaParser) Parse(rs io.ReadSeeker, size int) (mc riimage.MediaContext, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // TODO(dustin): Add test
+
+ ps := NewPngSplitter()
+
+ err = ps.readHeader(rs)
+ log.PanicIf(err)
+
+ s := bufio.NewScanner(rs)
+
+ // Since each segment can be any size, our buffer must be allowed to grow
+ // as large as the file.
+ buffer := []byte{}
+ s.Buffer(buffer, size)
+ s.Split(ps.Split)
+
+ for s.Scan() != false {
+ }
+
+ log.PanicIf(s.Err())
+
+ return ps.Chunks(), nil
+}
+
+// ParseFile parses a PNG stream given a file-path.
+func (pmp *PngMediaParser) ParseFile(filepath string) (mc riimage.MediaContext, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ f, err := os.Open(filepath)
+ log.PanicIf(err)
+
+ defer f.Close()
+
+ stat, err := f.Stat()
+ log.PanicIf(err)
+
+ size := stat.Size()
+
+ chunks, err := pmp.Parse(f, int(size))
+ log.PanicIf(err)
+
+ return chunks, nil
+}
+
+// ParseBytes parses a PNG stream given a byte-slice.
+func (pmp *PngMediaParser) ParseBytes(data []byte) (mc riimage.MediaContext, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // TODO(dustin): Add test
+
+ br := bytes.NewReader(data)
+
+ chunks, err := pmp.Parse(br, len(data))
+ log.PanicIf(err)
+
+ return chunks, nil
+}
+
+// LooksLikeFormat returns a boolean indicating whether the stream looks like a
+// PNG image.
+func (pmp *PngMediaParser) LooksLikeFormat(data []byte) bool {
+ return bytes.Compare(data[:len(PngSignature)], PngSignature[:]) == 0
+}
+
+// GetImage returns an image.Image-compatible struct.
+func (pmp *PngMediaParser) GetImage(r io.Reader) (img image.Image, err error) {
+ img, err = png.Decode(r)
+ log.PanicIf(err)
+
+ return img, nil
+}
+
+var (
+ // Enforce interface conformance.
+ _ riimage.MediaParser = new(PngMediaParser)
+)
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/png.go b/vendor/github.com/dsoprea/go-png-image-structure/v2/png.go
index 203bbf562..fbb022887 100644
--- a/vendor/github.com/dsoprea/go-png-image-structure/png.go
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/png.go
@@ -9,9 +9,10 @@ import (
"encoding/binary"
"hash/crc32"
- "github.com/dsoprea/go-exif/v2"
+ "github.com/dsoprea/go-exif/v3"
+ "github.com/dsoprea/go-exif/v3/common"
"github.com/dsoprea/go-logging"
- "github.com/dsoprea/go-utility/image"
+ "github.com/dsoprea/go-utility/v2/image"
)
var (
@@ -22,7 +23,6 @@ var (
var (
ErrNotPng = errors.New("not png data")
- ErrNoExif = errors.New("file does not have EXIF")
ErrCrcFailure = errors.New("crc failure")
)
@@ -111,7 +111,7 @@ func (cs *ChunkSlice) FindExif() (chunk *Chunk, err error) {
return chunks[0], nil
}
- log.Panic(ErrNoExif)
+ log.Panic(exif.ErrNoExif)
// Never called.
return nil, nil
@@ -128,7 +128,9 @@ func (cs *ChunkSlice) Exif() (rootIfd *exif.Ifd, data []byte, err error) {
chunk, err := cs.FindExif()
log.PanicIf(err)
- im := exif.NewIfdMappingWithStandard()
+ im, err := exifcommon.NewIfdMappingWithStandard()
+ log.PanicIf(err)
+
ti := exif.NewTagIndex()
// TODO(dustin): Refactor and support `exif.GetExifData()`.
@@ -180,7 +182,7 @@ func (cs *ChunkSlice) SetExif(ib *exif.IfdBuilder) (err error) {
exifChunk.Data = exifData
exifChunk.Length = uint32(len(exifData))
} else {
- if log.Is(err, ErrNoExif) != true {
+ if log.Is(err, exif.ErrNoExif) != true {
log.Panic(err)
}
diff --git a/vendor/github.com/dsoprea/go-png-image-structure/v2/testing_common.go b/vendor/github.com/dsoprea/go-png-image-structure/v2/testing_common.go
new file mode 100644
index 000000000..9df13a858
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/testing_common.go
@@ -0,0 +1,64 @@
+package pngstructure
+
+import (
+ "os"
+ "path"
+
+ "github.com/dsoprea/go-logging"
+)
+
+var (
+ assetsPath = ""
+)
+
+func getModuleRootPath() string {
+ moduleRootPath := os.Getenv("PNG_MODULE_ROOT_PATH")
+ if moduleRootPath != "" {
+ return moduleRootPath
+ }
+
+ currentWd, err := os.Getwd()
+ log.PanicIf(err)
+
+ currentPath := currentWd
+ visited := make([]string, 0)
+
+ for {
+ tryStampFilepath := path.Join(currentPath, ".MODULE_ROOT")
+
+ _, err := os.Stat(tryStampFilepath)
+ if err != nil && os.IsNotExist(err) != true {
+ log.Panic(err)
+ } else if err == nil {
+ break
+ }
+
+ visited = append(visited, tryStampFilepath)
+
+ currentPath = path.Dir(currentPath)
+ if currentPath == "/" {
+ log.Panicf("could not find module-root: %v", visited)
+ }
+ }
+
+ return currentPath
+}
+
+func getTestAssetsPath() string {
+ if assetsPath == "" {
+ moduleRootPath := getModuleRootPath()
+ assetsPath = path.Join(moduleRootPath, "assets")
+ }
+
+ return assetsPath
+}
+
+func getTestBasicImageFilepath() string {
+ assetsPath := getTestAssetsPath()
+ return path.Join(assetsPath, "libpng.png")
+}
+
+func getTestExifImageFilepath() string {
+ assetsPath := getTestAssetsPath()
+ return path.Join(assetsPath, "exif.png")
+}
diff --git a/vendor/github.com/dsoprea/go-exif/v2/common/utility.go b/vendor/github.com/dsoprea/go-png-image-structure/v2/utility.go
index 65165bf02..dbff145a6 100644
--- a/vendor/github.com/dsoprea/go-exif/v2/common/utility.go
+++ b/vendor/github.com/dsoprea/go-png-image-structure/v2/utility.go
@@ -1,14 +1,12 @@
-package exifcommon
+package pngstructure
import (
"bytes"
"fmt"
- "time"
"github.com/dsoprea/go-logging"
)
-// DumpBytes prints a list of hex-encoded bytes.
func DumpBytes(data []byte) {
fmt.Printf("DUMP: ")
for _, x := range data {
@@ -18,8 +16,6 @@ func DumpBytes(data []byte) {
fmt.Printf("\n")
}
-// DumpBytesClause prints a list like DumpBytes(), but encapsulated in
-// "[]byte { ... }".
func DumpBytesClause(data []byte) {
fmt.Printf("DUMP: ")
@@ -36,7 +32,6 @@ func DumpBytesClause(data []byte) {
fmt.Printf(" }\n")
}
-// DumpBytesToString returns a stringified list of hex-encoded bytes.
func DumpBytesToString(data []byte) string {
b := new(bytes.Buffer)
@@ -53,7 +48,6 @@ func DumpBytesToString(data []byte) string {
return b.String()
}
-// DumpBytesClauseToString returns a comma-separated list of hex-encoded bytes.
func DumpBytesClauseToString(data []byte) string {
b := new(bytes.Buffer)
@@ -69,11 +63,3 @@ func DumpBytesClauseToString(data []byte) string {
return b.String()
}
-
-// ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a
-// `time.Time` struct. It will attempt to convert to UTC first.
-func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
- t = t.UTC()
-
- return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
-}
diff --git a/vendor/github.com/dsoprea/go-utility/LICENSE b/vendor/github.com/dsoprea/go-utility/v2/LICENSE
index 8941063e1..8941063e1 100644
--- a/vendor/github.com/dsoprea/go-utility/LICENSE
+++ b/vendor/github.com/dsoprea/go-utility/v2/LICENSE
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/README.md b/vendor/github.com/dsoprea/go-utility/v2/filesystem/README.md
new file mode 100644
index 000000000..eb03fea7c
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/README.md
@@ -0,0 +1,64 @@
+[![GoDoc](https://godoc.org/github.com/dsoprea/go-utility/filesystem?status.svg)](https://godoc.org/github.com/dsoprea/go-utility/filesystem)
+[![Build Status](https://travis-ci.org/dsoprea/go-utility.svg?branch=master)](https://travis-ci.org/dsoprea/go-utility)
+[![Coverage Status](https://coveralls.io/repos/github/dsoprea/go-utility/badge.svg?branch=master)](https://coveralls.io/github/dsoprea/go-utility?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/dsoprea/go-utility)](https://goreportcard.com/report/github.com/dsoprea/go-utility)
+
+# bounceback
+
+An `io.ReadSeeker` and `io.WriteSeeker` that returns to the right place before
+reading or writing. Useful when the same file resource is being reused for reads
+or writes throughout that file.
+
+# list_files
+
+A recursive path walker that supports filters.
+
+# seekable_buffer
+
+A memory structure that satisfies `io.ReadWriteSeeker`.
+
+# copy_bytes_between_positions
+
+Given an `io.ReadWriteSeeker`, copy N bytes from one position to an earlier
+position.
+
+# read_counter, write_counter
+
+Wrap `io.Reader` and `io.Writer` structs in order to report how many bytes were
+transferred.
+
+# readseekwritecloser
+
+Provides the ReadWriteSeekCloser interface that combines a RWS and a Closer.
+Also provides a no-op wrapper to augment a plain RWS with a closer.
+
+# boundedreadwriteseek
+
+Wraps a ReadWriteSeeker such that no seeks can be at an offset less than a
+specific-offset.
+
+# calculateseek
+
+Provides a reusable function with which to calculate seek offsets.
+
+# progress_wrapper
+
+Provides `io.Reader` and `io.Writer` wrappers that also trigger callbacks after
+each call. The reader wrapper also invokes the callback upon EOF.
+
+# does_exist
+
+Check whether a file/directory exists using a file-path.
+
+# graceful_copy
+
+Do a copy but correctly handle short-writes and reads that might return a non-
+zero read count *and* EOF.
+
+# readseeker_to_readerat
+
+A wrapper that allows an `io.ReadSeeker` to be used as a `io.ReaderAt`.
+
+# simplefileinfo
+
+An implementation of `os.FileInfo` to support testing.
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/bounceback.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/bounceback.go
new file mode 100644
index 000000000..1112a10ef
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/bounceback.go
@@ -0,0 +1,273 @@
+package rifs
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// BouncebackStats describes operation counts.
+type BouncebackStats struct {
+ reads int
+ writes int
+ seeks int
+ syncs int
+}
+
+func (bbs BouncebackStats) String() string {
+ return fmt.Sprintf(
+ "BouncebackStats<READS=(%d) WRITES=(%d) SEEKS=(%d) SYNCS=(%d)>",
+ bbs.reads, bbs.writes, bbs.seeks, bbs.syncs)
+}
+
+type bouncebackBase struct {
+ currentPosition int64
+
+ stats BouncebackStats
+}
+
+// Position returns the position that we're supposed to be at.
+func (bb *bouncebackBase) Position() int64 {
+
+ // TODO(dustin): Add test
+
+ return bb.currentPosition
+}
+
+// StatsReads returns the number of reads that have been attempted.
+func (bb *bouncebackBase) StatsReads() int {
+
+ // TODO(dustin): Add test
+
+ return bb.stats.reads
+}
+
+// StatsWrites returns the number of write operations.
+func (bb *bouncebackBase) StatsWrites() int {
+
+ // TODO(dustin): Add test
+
+ return bb.stats.writes
+}
+
+// StatsSeeks returns the number of seeks.
+func (bb *bouncebackBase) StatsSeeks() int {
+
+ // TODO(dustin): Add test
+
+ return bb.stats.seeks
+}
+
+// StatsSyncs returns the number of corrective seeks ("bounce-backs").
+func (bb *bouncebackBase) StatsSyncs() int {
+
+ // TODO(dustin): Add test
+
+ return bb.stats.syncs
+}
+
+// Seek does a seek to an arbitrary place in the `io.ReadSeeker`.
+func (bb *bouncebackBase) seek(s io.Seeker, offset int64, whence int) (newPosition int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // If the seek is relative, make sure we're where we're supposed to be *first*.
+ if whence != io.SeekStart {
+ err = bb.checkPosition(s)
+ log.PanicIf(err)
+ }
+
+ bb.stats.seeks++
+
+ newPosition, err = s.Seek(offset, whence)
+ log.PanicIf(err)
+
+ // Update our internal tracking.
+ bb.currentPosition = newPosition
+
+ return newPosition, nil
+}
+
+func (bb *bouncebackBase) checkPosition(s io.Seeker) (err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // Make sure we're where we're supposed to be.
+
+ // This should have no overhead, and enables us to collect stats.
+ realCurrentPosition, err := s.Seek(0, io.SeekCurrent)
+ log.PanicIf(err)
+
+ if realCurrentPosition != bb.currentPosition {
+ bb.stats.syncs++
+
+ _, err = s.Seek(bb.currentPosition, io.SeekStart)
+ log.PanicIf(err)
+ }
+
+ return nil
+}
+
+// BouncebackReader wraps a ReadSeeker, keeps track of our position, and
+// seeks back to it before writing. This allows an underlying ReadWriteSeeker
+// with an unstable position can still be used for a prolonged series of writes.
+type BouncebackReader struct {
+ rs io.ReadSeeker
+
+ bouncebackBase
+}
+
+// NewBouncebackReader returns a `*BouncebackReader` struct.
+func NewBouncebackReader(rs io.ReadSeeker) (br *BouncebackReader, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ initialPosition, err := rs.Seek(0, io.SeekCurrent)
+ log.PanicIf(err)
+
+ bb := bouncebackBase{
+ currentPosition: initialPosition,
+ }
+
+ br = &BouncebackReader{
+ rs: rs,
+ bouncebackBase: bb,
+ }
+
+ return br, nil
+}
+
+// Seek does a seek to an arbitrary place in the `io.ReadSeeker`.
+func (br *BouncebackReader) Seek(offset int64, whence int) (newPosition int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ newPosition, err = br.bouncebackBase.seek(br.rs, offset, whence)
+ log.PanicIf(err)
+
+ return newPosition, nil
+}
+
+// Seek does a standard read.
+func (br *BouncebackReader) Read(p []byte) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ br.bouncebackBase.stats.reads++
+
+ err = br.bouncebackBase.checkPosition(br.rs)
+ log.PanicIf(err)
+
+ // Do read.
+
+ n, err = br.rs.Read(p)
+ if err != nil {
+ if err == io.EOF {
+ return 0, io.EOF
+ }
+
+ log.Panic(err)
+ }
+
+ // Update our internal tracking.
+ br.bouncebackBase.currentPosition += int64(n)
+
+ return n, nil
+}
+
+// BouncebackWriter wraps a WriteSeeker, keeps track of our position, and
+// seeks back to it before writing. This allows an underlying ReadWriteSeeker
+// with an unstable position can still be used for a prolonged series of writes.
+type BouncebackWriter struct {
+ ws io.WriteSeeker
+
+ bouncebackBase
+}
+
+// NewBouncebackWriter returns a new `BouncebackWriter` struct.
+func NewBouncebackWriter(ws io.WriteSeeker) (bw *BouncebackWriter, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ initialPosition, err := ws.Seek(0, io.SeekCurrent)
+ log.PanicIf(err)
+
+ bb := bouncebackBase{
+ currentPosition: initialPosition,
+ }
+
+ bw = &BouncebackWriter{
+ ws: ws,
+ bouncebackBase: bb,
+ }
+
+ return bw, nil
+}
+
+// Seek puts us at a specific position in the internal writer for the next
+// write/seek.
+func (bw *BouncebackWriter) Seek(offset int64, whence int) (newPosition int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ newPosition, err = bw.bouncebackBase.seek(bw.ws, offset, whence)
+ log.PanicIf(err)
+
+ return newPosition, nil
+}
+
+// Write performs a write against the internal `WriteSeeker` starting at the
+// position that we're supposed to be at.
+func (bw *BouncebackWriter) Write(p []byte) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ bw.bouncebackBase.stats.writes++
+
+ // Make sure we're where we're supposed to be.
+
+ realCurrentPosition, err := bw.ws.Seek(0, io.SeekCurrent)
+ log.PanicIf(err)
+
+ if realCurrentPosition != bw.bouncebackBase.currentPosition {
+ bw.bouncebackBase.stats.seeks++
+
+ _, err = bw.ws.Seek(bw.bouncebackBase.currentPosition, io.SeekStart)
+ log.PanicIf(err)
+ }
+
+ // Do write.
+
+ n, err = bw.ws.Write(p)
+ log.PanicIf(err)
+
+ // Update our internal tracking.
+ bw.bouncebackBase.currentPosition += int64(n)
+
+ return n, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseekcloser.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseekcloser.go
new file mode 100644
index 000000000..3d2e840fa
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseekcloser.go
@@ -0,0 +1,95 @@
+package rifs
+
+import (
+ "io"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// BoundedReadWriteSeekCloser wraps a RWS that is also a closer with boundaries.
+// This proxies the RWS methods to the inner BRWS inside.
+type BoundedReadWriteSeekCloser struct {
+ io.Closer
+ *BoundedReadWriteSeeker
+}
+
+// NewBoundedReadWriteSeekCloser returns a new BoundedReadWriteSeekCloser.
+func NewBoundedReadWriteSeekCloser(rwsc ReadWriteSeekCloser, minimumOffset int64, staticFileSize int64) (brwsc *BoundedReadWriteSeekCloser, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ bs, err := NewBoundedReadWriteSeeker(rwsc, minimumOffset, staticFileSize)
+ log.PanicIf(err)
+
+ brwsc = &BoundedReadWriteSeekCloser{
+ Closer: rwsc,
+ BoundedReadWriteSeeker: bs,
+ }
+
+ return brwsc, nil
+}
+
+// Seek forwards calls to the inner RWS.
+func (rwsc *BoundedReadWriteSeekCloser) Seek(offset int64, whence int) (newOffset int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ newOffset, err = rwsc.BoundedReadWriteSeeker.Seek(offset, whence)
+ log.PanicIf(err)
+
+ return newOffset, nil
+}
+
+// Read forwards calls to the inner RWS.
+func (rwsc *BoundedReadWriteSeekCloser) Read(buffer []byte) (readCount int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ readCount, err = rwsc.BoundedReadWriteSeeker.Read(buffer)
+ if err != nil {
+ if err == io.EOF {
+ return 0, err
+ }
+
+ log.Panic(err)
+ }
+
+ return readCount, nil
+}
+
+// Write forwards calls to the inner RWS.
+func (rwsc *BoundedReadWriteSeekCloser) Write(buffer []byte) (writtenCount int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ writtenCount, err = rwsc.BoundedReadWriteSeeker.Write(buffer)
+ log.PanicIf(err)
+
+ return writtenCount, nil
+}
+
+// Close forwards calls to the inner RWS.
+func (rwsc *BoundedReadWriteSeekCloser) Close() (err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ err = rwsc.Closer.Close()
+ log.PanicIf(err)
+
+ return nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseeker.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseeker.go
new file mode 100644
index 000000000..d29657b05
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/boundedreadwriteseeker.go
@@ -0,0 +1,156 @@
+package rifs
+
+import (
+ "errors"
+ "io"
+ "os"
+
+ "github.com/dsoprea/go-logging"
+)
+
+var (
+ // ErrSeekBeyondBound is returned when a seek is requested beyond the
+ // statically-given file-size. No writes or seeks beyond boundaries are
+ // supported with a statically-given file size.
+ ErrSeekBeyondBound = errors.New("seek beyond boundary")
+)
+
+// BoundedReadWriteSeeker is a thin filter that ensures that no seeks can be done
+// to offsets smaller than the one we were given. This supports libraries that
+// might be expecting to read from the front of the stream being used on data
+// that is in the middle of a stream instead.
+type BoundedReadWriteSeeker struct {
+ io.ReadWriteSeeker
+
+ currentOffset int64
+ minimumOffset int64
+
+ staticFileSize int64
+}
+
+// NewBoundedReadWriteSeeker returns a new BoundedReadWriteSeeker instance.
+func NewBoundedReadWriteSeeker(rws io.ReadWriteSeeker, minimumOffset int64, staticFileSize int64) (brws *BoundedReadWriteSeeker, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if minimumOffset < 0 {
+ log.Panicf("BoundedReadWriteSeeker minimum offset must be zero or larger: (%d)", minimumOffset)
+ }
+
+ // We'll always started at a relative offset of zero.
+ _, err = rws.Seek(minimumOffset, os.SEEK_SET)
+ log.PanicIf(err)
+
+ brws = &BoundedReadWriteSeeker{
+ ReadWriteSeeker: rws,
+
+ currentOffset: 0,
+ minimumOffset: minimumOffset,
+
+ staticFileSize: staticFileSize,
+ }
+
+ return brws, nil
+}
+
+// Seek moves the offset to the given offset. Prevents offset from ever being
+// moved left of `brws.minimumOffset`.
+func (brws *BoundedReadWriteSeeker) Seek(offset int64, whence int) (updatedOffset int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ fileSize := brws.staticFileSize
+
+ // If we weren't given a static file-size, look it up whenever it is needed.
+ if whence == os.SEEK_END && fileSize == 0 {
+ realFileSizeRaw, err := brws.ReadWriteSeeker.Seek(0, os.SEEK_END)
+ log.PanicIf(err)
+
+ fileSize = realFileSizeRaw - brws.minimumOffset
+ }
+
+ updatedOffset, err = CalculateSeek(brws.currentOffset, offset, whence, fileSize)
+ log.PanicIf(err)
+
+ if brws.staticFileSize != 0 && updatedOffset > brws.staticFileSize {
+ //updatedOffset = int64(brws.staticFileSize)
+
+ // NOTE(dustin): Presumably, this will only be disruptive to writes that are beyond the boundaries, which, if we're being used at all, should already account for the boundary and prevent this error from ever happening. So, time will tell how disruptive this is.
+ return 0, ErrSeekBeyondBound
+ }
+
+ if updatedOffset != brws.currentOffset {
+ updatedRealOffset := updatedOffset + brws.minimumOffset
+
+ _, err = brws.ReadWriteSeeker.Seek(updatedRealOffset, os.SEEK_SET)
+ log.PanicIf(err)
+
+ brws.currentOffset = updatedOffset
+ }
+
+ return updatedOffset, nil
+}
+
+// Read forwards writes to the inner RWS.
+func (brws *BoundedReadWriteSeeker) Read(buffer []byte) (readCount int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if brws.staticFileSize != 0 {
+ availableCount := brws.staticFileSize - brws.currentOffset
+ if availableCount == 0 {
+ return 0, io.EOF
+ }
+
+ if int64(len(buffer)) > availableCount {
+ buffer = buffer[:availableCount]
+ }
+ }
+
+ readCount, err = brws.ReadWriteSeeker.Read(buffer)
+ brws.currentOffset += int64(readCount)
+
+ if err != nil {
+ if err == io.EOF {
+ return 0, err
+ }
+
+ log.Panic(err)
+ }
+
+ return readCount, nil
+}
+
+// Write forwards writes to the inner RWS.
+func (brws *BoundedReadWriteSeeker) Write(buffer []byte) (writtenCount int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if brws.staticFileSize != 0 {
+ log.Panicf("writes can not be performed if a static file-size was given")
+ }
+
+ writtenCount, err = brws.ReadWriteSeeker.Write(buffer)
+ brws.currentOffset += int64(writtenCount)
+
+ log.PanicIf(err)
+
+ return writtenCount, nil
+}
+
+// MinimumOffset returns the configured minimum-offset.
+func (brws *BoundedReadWriteSeeker) MinimumOffset() int64 {
+ return brws.minimumOffset
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/calculate_seek.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/calculate_seek.go
new file mode 100644
index 000000000..cd59d727c
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/calculate_seek.go
@@ -0,0 +1,52 @@
+package rifs
+
+import (
+ "io"
+ "os"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// SeekType is a convenience type to associate the different seek-types with
+// printable descriptions.
+type SeekType int
+
+// String returns a descriptive string.
+func (n SeekType) String() string {
+ if n == io.SeekCurrent {
+ return "SEEK-CURRENT"
+ } else if n == io.SeekEnd {
+ return "SEEK-END"
+ } else if n == io.SeekStart {
+ return "SEEK-START"
+ }
+
+ log.Panicf("unknown seek-type: (%d)", n)
+ return ""
+}
+
+// CalculateSeek calculates an offset in a file-stream given the parameters.
+func CalculateSeek(currentOffset int64, delta int64, whence int, fileSize int64) (finalOffset int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ finalOffset = 0
+ }
+ }()
+
+ if whence == os.SEEK_SET {
+ finalOffset = delta
+ } else if whence == os.SEEK_CUR {
+ finalOffset = currentOffset + delta
+ } else if whence == os.SEEK_END {
+ finalOffset = fileSize + delta
+ } else {
+ log.Panicf("whence not valid: (%d)", whence)
+ }
+
+ if finalOffset < 0 {
+ finalOffset = 0
+ }
+
+ return finalOffset, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/common.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/common.go
new file mode 100644
index 000000000..256333d40
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/common.go
@@ -0,0 +1,15 @@
+package rifs
+
+import (
+ "os"
+ "path"
+)
+
+var (
+ appPath string
+)
+
+func init() {
+ goPath := os.Getenv("GOPATH")
+ appPath = path.Join(goPath, "src", "github.com", "dsoprea", "go-utility", "filesystem")
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/copy_bytes_between_positions.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/copy_bytes_between_positions.go
new file mode 100644
index 000000000..89ee9a92c
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/copy_bytes_between_positions.go
@@ -0,0 +1,40 @@
+package rifs
+
+import (
+ "io"
+ "os"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// CopyBytesBetweenPositions will copy bytes from one position in the given RWS
+// to an earlier position in the same RWS.
+func CopyBytesBetweenPositions(rws io.ReadWriteSeeker, fromPosition, toPosition int64, count int) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if fromPosition <= toPosition {
+ log.Panicf("from position (%d) must be larger than to position (%d)", fromPosition, toPosition)
+ }
+
+ br, err := NewBouncebackReader(rws)
+ log.PanicIf(err)
+
+ _, err = br.Seek(fromPosition, os.SEEK_SET)
+ log.PanicIf(err)
+
+ bw, err := NewBouncebackWriter(rws)
+ log.PanicIf(err)
+
+ _, err = bw.Seek(toPosition, os.SEEK_SET)
+ log.PanicIf(err)
+
+ written, err := io.CopyN(bw, br, int64(count))
+ log.PanicIf(err)
+
+ n = int(written)
+ return n, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/does_exist.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/does_exist.go
new file mode 100644
index 000000000..f5e6cd20a
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/does_exist.go
@@ -0,0 +1,19 @@
+package rifs
+
+import (
+ "os"
+)
+
+// DoesExist returns true if we can open the given file/path without error. We
+// can't simply use `os.IsNotExist()` because we'll get a different error when
+// the parent directory doesn't exist, and really the only important thing is if
+// it exists *and* it's readable.
+func DoesExist(filepath string) bool {
+ f, err := os.Open(filepath)
+ if err != nil {
+ return false
+ }
+
+ f.Close()
+ return true
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/graceful_copy.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/graceful_copy.go
new file mode 100644
index 000000000..8705e5fe0
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/graceful_copy.go
@@ -0,0 +1,54 @@
+package rifs
+
+import (
+ "fmt"
+ "io"
+)
+
+const (
+ defaultCopyBufferSize = 1024 * 1024
+)
+
+// GracefulCopy willcopy while enduring lesser normal issues.
+//
+// - We'll ignore EOF if the read byte-count is more than zero. Only an EOF when
+// zero bytes were read will terminate the loop.
+//
+// - Ignore short-writes. If less bytes were written than the bytes that were
+// given, we'll keep trying until done.
+func GracefulCopy(w io.Writer, r io.Reader, buffer []byte) (copyCount int, err error) {
+ if buffer == nil {
+ buffer = make([]byte, defaultCopyBufferSize)
+ }
+
+ for {
+ readCount, err := r.Read(buffer)
+ if err != nil {
+ if err != io.EOF {
+ err = fmt.Errorf("read error: %s", err.Error())
+ return 0, err
+ }
+
+ // Only break on EOF if no bytes were actually read.
+ if readCount == 0 {
+ break
+ }
+ }
+
+ writeBuffer := buffer[:readCount]
+
+ for len(writeBuffer) > 0 {
+ writtenCount, err := w.Write(writeBuffer)
+ if err != nil {
+ err = fmt.Errorf("write error: %s", err.Error())
+ return 0, err
+ }
+
+ writeBuffer = writeBuffer[writtenCount:]
+ }
+
+ copyCount += readCount
+ }
+
+ return copyCount, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/list_files.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/list_files.go
new file mode 100644
index 000000000..bcdbd67cb
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/list_files.go
@@ -0,0 +1,143 @@
+package rifs
+
+import (
+ "io"
+ "os"
+ "path"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// FileListFilterPredicate is the callback predicate used for filtering.
+type FileListFilterPredicate func(parent string, child os.FileInfo) (hit bool, err error)
+
+// VisitedFile is one visited file.
+type VisitedFile struct {
+ Filepath string
+ Info os.FileInfo
+ Index int
+}
+
+// ListFiles feeds a continuous list of files from a recursive folder scan. An
+// optional predicate can be provided in order to filter. When done, the
+// `filesC` channel is closed. If there's an error, the `errC` channel will
+// receive it.
+func ListFiles(rootPath string, cb FileListFilterPredicate) (filesC chan VisitedFile, count int, errC chan error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err := log.Wrap(state.(error))
+ log.Panic(err)
+ }
+ }()
+
+ // Make sure the path exists.
+
+ f, err := os.Open(rootPath)
+ log.PanicIf(err)
+
+ f.Close()
+
+ // Do our thing.
+
+ filesC = make(chan VisitedFile, 100)
+ errC = make(chan error, 1)
+ index := 0
+
+ go func() {
+ defer func() {
+ if state := recover(); state != nil {
+ err := log.Wrap(state.(error))
+ errC <- err
+ }
+ }()
+
+ queue := []string{rootPath}
+ for len(queue) > 0 {
+ // Pop the next folder to process off the queue.
+ var thisPath string
+ thisPath, queue = queue[0], queue[1:]
+
+ // Skip path if a symlink.
+
+ fi, err := os.Lstat(thisPath)
+ log.PanicIf(err)
+
+ if (fi.Mode() & os.ModeSymlink) > 0 {
+ continue
+ }
+
+ // Read information.
+
+ folderF, err := os.Open(thisPath)
+ if err != nil {
+ errC <- log.Wrap(err)
+ return
+ }
+
+ // Iterate through children.
+
+ for {
+ children, err := folderF.Readdir(1000)
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ errC <- log.Wrap(err)
+ return
+ }
+
+ for _, child := range children {
+ filepath := path.Join(thisPath, child.Name())
+
+ // Skip if a file symlink.
+
+ fi, err := os.Lstat(filepath)
+ log.PanicIf(err)
+
+ if (fi.Mode() & os.ModeSymlink) > 0 {
+ continue
+ }
+
+ // If a predicate was given, determine if this child will be
+ // left behind.
+ if cb != nil {
+ hit, err := cb(thisPath, child)
+
+ if err != nil {
+ errC <- log.Wrap(err)
+ return
+ }
+
+ if hit == false {
+ continue
+ }
+ }
+
+ index++
+
+ // Push file to channel.
+
+ vf := VisitedFile{
+ Filepath: filepath,
+ Info: child,
+ Index: index,
+ }
+
+ filesC <- vf
+
+ // If a folder, queue for later processing.
+
+ if child.IsDir() == true {
+ queue = append(queue, filepath)
+ }
+ }
+ }
+
+ folderF.Close()
+ }
+
+ close(filesC)
+ close(errC)
+ }()
+
+ return filesC, index, errC
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/progress_wrapper.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/progress_wrapper.go
new file mode 100644
index 000000000..0a064c53d
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/progress_wrapper.go
@@ -0,0 +1,93 @@
+package rifs
+
+import (
+ "io"
+ "time"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// ProgressFunc receives progress updates.
+type ProgressFunc func(n int, duration time.Duration, isEof bool) error
+
+// WriteProgressWrapper wraps a reader and calls a callback after each read with
+// count and duration info.
+type WriteProgressWrapper struct {
+ w io.Writer
+ progressCb ProgressFunc
+}
+
+// NewWriteProgressWrapper returns a new WPW instance.
+func NewWriteProgressWrapper(w io.Writer, progressCb ProgressFunc) io.Writer {
+ return &WriteProgressWrapper{
+ w: w,
+ progressCb: progressCb,
+ }
+}
+
+// Write does a write and calls the callback.
+func (wpw *WriteProgressWrapper) Write(buffer []byte) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ startAt := time.Now()
+
+ n, err = wpw.w.Write(buffer)
+ log.PanicIf(err)
+
+ duration := time.Since(startAt)
+
+ err = wpw.progressCb(n, duration, false)
+ log.PanicIf(err)
+
+ return n, nil
+}
+
+// ReadProgressWrapper wraps a reader and calls a callback after each read with
+// count and duration info.
+type ReadProgressWrapper struct {
+ r io.Reader
+ progressCb ProgressFunc
+}
+
+// NewReadProgressWrapper returns a new RPW instance.
+func NewReadProgressWrapper(r io.Reader, progressCb ProgressFunc) io.Reader {
+ return &ReadProgressWrapper{
+ r: r,
+ progressCb: progressCb,
+ }
+}
+
+// Read reads data and calls the callback.
+func (rpw *ReadProgressWrapper) Read(buffer []byte) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ startAt := time.Now()
+
+ n, err = rpw.r.Read(buffer)
+
+ duration := time.Since(startAt)
+
+ if err != nil {
+ if err == io.EOF {
+ errInner := rpw.progressCb(n, duration, true)
+ log.PanicIf(errInner)
+
+ return n, err
+ }
+
+ log.Panic(err)
+ }
+
+ err = rpw.progressCb(n, duration, false)
+ log.PanicIf(err)
+
+ return n, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/read_counter.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/read_counter.go
new file mode 100644
index 000000000..d878ca4e6
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/read_counter.go
@@ -0,0 +1,36 @@
+package rifs
+
+import (
+ "io"
+)
+
+// ReadCounter proxies read requests and maintains a counter of bytes read.
+type ReadCounter struct {
+ r io.Reader
+ counter int
+}
+
+// NewReadCounter returns a new `ReadCounter` struct wrapping a `Reader`.
+func NewReadCounter(r io.Reader) *ReadCounter {
+ return &ReadCounter{
+ r: r,
+ }
+}
+
+// Count returns the total number of bytes read.
+func (rc *ReadCounter) Count() int {
+ return rc.counter
+}
+
+// Reset resets the counter to zero.
+func (rc *ReadCounter) Reset() {
+ rc.counter = 0
+}
+
+// Read forwards a read to the underlying `Reader` while bumping the counter.
+func (rc *ReadCounter) Read(b []byte) (n int, err error) {
+ n, err = rc.r.Read(b)
+ rc.counter += n
+
+ return n, err
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/readseeker_to_readerat.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/readseeker_to_readerat.go
new file mode 100644
index 000000000..3f3ec44dd
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/readseeker_to_readerat.go
@@ -0,0 +1,63 @@
+package rifs
+
+import (
+ "io"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// ReadSeekerToReaderAt is a wrapper that allows a ReadSeeker to masquerade as a
+// ReaderAt.
+type ReadSeekerToReaderAt struct {
+ rs io.ReadSeeker
+}
+
+// NewReadSeekerToReaderAt returns a new ReadSeekerToReaderAt instance.
+func NewReadSeekerToReaderAt(rs io.ReadSeeker) *ReadSeekerToReaderAt {
+ return &ReadSeekerToReaderAt{
+ rs: rs,
+ }
+}
+
+// ReadAt is a wrapper that satisfies the ReaderAt interface.
+//
+// Note that a requirement of ReadAt is that it doesn't have an effect on the
+// offset in the underlying resource as well as that concurrent calls can be
+// made to it. Since we're capturing the current offset in the underlying
+// resource and then seeking back to it before returning, it is the
+// responsibility of the caller to serialize (i.e. use a mutex with) these
+// requests in order to eliminate race-conditions in the parallel-usage
+// scenario.
+//
+// Note also that, since ReadAt() is going to be called on a particular
+// instance, that instance is going to internalize a file resource, that file-
+// resource is provided by the OS, and [most] OSs are only gonna support one
+// file-position per resource, locking is already going to be a necessary
+// internal semantic of a ReaderAt implementation.
+func (rstra *ReadSeekerToReaderAt) ReadAt(p []byte, offset int64) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ originalOffset, err := rstra.rs.Seek(0, io.SeekCurrent)
+ log.PanicIf(err)
+
+ defer func() {
+ _, err := rstra.rs.Seek(originalOffset, io.SeekStart)
+ log.PanicIf(err)
+ }()
+
+ _, err = rstra.rs.Seek(offset, io.SeekStart)
+ log.PanicIf(err)
+
+ // Note that all errors will be wrapped, here. The usage of this method is
+ // such that typically no specific errors would be expected as part of
+ // normal operation (in which case we'd check for those first and return
+ // them directly).
+ n, err = io.ReadFull(rstra.rs, p)
+ log.PanicIf(err)
+
+ return n, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/readwriteseekcloser.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/readwriteseekcloser.go
new file mode 100644
index 000000000..c583a8024
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/readwriteseekcloser.go
@@ -0,0 +1,29 @@
+package rifs
+
+import (
+ "io"
+)
+
+// ReadWriteSeekCloser satisfies `io.ReadWriteSeeker` and `io.Closer`
+// interfaces.
+type ReadWriteSeekCloser interface {
+ io.ReadWriteSeeker
+ io.Closer
+}
+
+type readWriteSeekNoopCloser struct {
+ io.ReadWriteSeeker
+}
+
+// ReadWriteSeekNoopCloser wraps a `io.ReadWriteSeeker` with a no-op Close()
+// call.
+func ReadWriteSeekNoopCloser(rws io.ReadWriteSeeker) ReadWriteSeekCloser {
+ return readWriteSeekNoopCloser{
+ ReadWriteSeeker: rws,
+ }
+}
+
+// Close does nothing but allows the RWS to satisfy `io.Closer`.:wq
+func (readWriteSeekNoopCloser) Close() (err error) {
+ return nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/seekable_buffer.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/seekable_buffer.go
new file mode 100644
index 000000000..5d41bb5df
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/seekable_buffer.go
@@ -0,0 +1,146 @@
+package rifs
+
+import (
+ "io"
+ "os"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// SeekableBuffer is a simple memory structure that satisfies
+// `io.ReadWriteSeeker`.
+type SeekableBuffer struct {
+ data []byte
+ position int64
+}
+
+// NewSeekableBuffer is a factory that returns a `*SeekableBuffer`.
+func NewSeekableBuffer() *SeekableBuffer {
+ data := make([]byte, 0)
+
+ return &SeekableBuffer{
+ data: data,
+ }
+}
+
+// NewSeekableBufferWithBytes is a factory that returns a `*SeekableBuffer`.
+func NewSeekableBufferWithBytes(originalData []byte) *SeekableBuffer {
+ data := make([]byte, len(originalData))
+ copy(data, originalData)
+
+ return &SeekableBuffer{
+ data: data,
+ }
+}
+
+func len64(data []byte) int64 {
+ return int64(len(data))
+}
+
+// Bytes returns the underlying slice.
+func (sb *SeekableBuffer) Bytes() []byte {
+ return sb.data
+}
+
+// Len returns the number of bytes currently stored.
+func (sb *SeekableBuffer) Len() int {
+ return len(sb.data)
+}
+
+// Write does a standard write to the internal slice.
+func (sb *SeekableBuffer) Write(p []byte) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ // The current position we're already at is past the end of the data we
+ // actually have. Extend our buffer up to our current position.
+ if sb.position > len64(sb.data) {
+ extra := make([]byte, sb.position-len64(sb.data))
+ sb.data = append(sb.data, extra...)
+ }
+
+ positionFromEnd := len64(sb.data) - sb.position
+ tailCount := positionFromEnd - len64(p)
+
+ var tailBytes []byte
+ if tailCount > 0 {
+ tailBytes = sb.data[len64(sb.data)-tailCount:]
+ sb.data = append(sb.data[:sb.position], p...)
+ } else {
+ sb.data = append(sb.data[:sb.position], p...)
+ }
+
+ if tailBytes != nil {
+ sb.data = append(sb.data, tailBytes...)
+ }
+
+ dataSize := len64(p)
+ sb.position += dataSize
+
+ return int(dataSize), nil
+}
+
+// Read does a standard read against the internal slice.
+func (sb *SeekableBuffer) Read(p []byte) (n int, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if sb.position >= len64(sb.data) {
+ return 0, io.EOF
+ }
+
+ n = copy(p, sb.data[sb.position:])
+ sb.position += int64(n)
+
+ return n, nil
+}
+
+// Truncate either chops or extends the internal buffer.
+func (sb *SeekableBuffer) Truncate(size int64) (err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ sizeInt := int(size)
+ if sizeInt < len(sb.data)-1 {
+ sb.data = sb.data[:sizeInt]
+ } else {
+ new := make([]byte, sizeInt-len(sb.data))
+ sb.data = append(sb.data, new...)
+ }
+
+ return nil
+}
+
+// Seek does a standard seek on the internal slice.
+func (sb *SeekableBuffer) Seek(offset int64, whence int) (n int64, err error) {
+ defer func() {
+ if state := recover(); state != nil {
+ err = log.Wrap(state.(error))
+ }
+ }()
+
+ if whence == os.SEEK_SET {
+ sb.position = offset
+ } else if whence == os.SEEK_END {
+ sb.position = len64(sb.data) + offset
+ } else if whence == os.SEEK_CUR {
+ sb.position += offset
+ } else {
+ log.Panicf("seek whence is not valid: (%d)", whence)
+ }
+
+ if sb.position < 0 {
+ sb.position = 0
+ }
+
+ return sb.position, nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/simplefileinfo.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/simplefileinfo.go
new file mode 100644
index 000000000..a227b0b00
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/simplefileinfo.go
@@ -0,0 +1,69 @@
+package rifs
+
+import (
+ "os"
+ "time"
+)
+
+// SimpleFileInfo is a simple `os.FileInfo` implementation useful for testing
+// with the bare minimum.
+type SimpleFileInfo struct {
+ filename string
+ isDir bool
+ size int64
+ mode os.FileMode
+ modTime time.Time
+}
+
+// NewSimpleFileInfoWithFile returns a new file-specific SimpleFileInfo.
+func NewSimpleFileInfoWithFile(filename string, size int64, mode os.FileMode, modTime time.Time) *SimpleFileInfo {
+ return &SimpleFileInfo{
+ filename: filename,
+ isDir: false,
+ size: size,
+ mode: mode,
+ modTime: modTime,
+ }
+}
+
+// NewSimpleFileInfoWithDirectory returns a new directory-specific
+// SimpleFileInfo.
+func NewSimpleFileInfoWithDirectory(filename string, modTime time.Time) *SimpleFileInfo {
+ return &SimpleFileInfo{
+ filename: filename,
+ isDir: true,
+ mode: os.ModeDir,
+ modTime: modTime,
+ }
+}
+
+// Name returns the base name of the file.
+func (sfi *SimpleFileInfo) Name() string {
+ return sfi.filename
+}
+
+// Size returns the length in bytes for regular files; system-dependent for
+// others.
+func (sfi *SimpleFileInfo) Size() int64 {
+ return sfi.size
+}
+
+// Mode returns the file mode bits.
+func (sfi *SimpleFileInfo) Mode() os.FileMode {
+ return sfi.mode
+}
+
+// ModTime returns the modification time.
+func (sfi *SimpleFileInfo) ModTime() time.Time {
+ return sfi.modTime
+}
+
+// IsDir returns true if a directory.
+func (sfi *SimpleFileInfo) IsDir() bool {
+ return sfi.isDir
+}
+
+// Sys returns internal state.
+func (sfi *SimpleFileInfo) Sys() interface{} {
+ return nil
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/utility.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/utility.go
new file mode 100644
index 000000000..4b33b41a9
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/utility.go
@@ -0,0 +1,17 @@
+package rifs
+
+import (
+ "io"
+ "os"
+
+ "github.com/dsoprea/go-logging"
+)
+
+// GetOffset returns the current offset of the Seeker and just panics if unable
+// to find it.
+func GetOffset(s io.Seeker) int64 {
+ offsetRaw, err := s.Seek(0, os.SEEK_CUR)
+ log.PanicIf(err)
+
+ return offsetRaw
+}
diff --git a/vendor/github.com/dsoprea/go-utility/v2/filesystem/write_counter.go b/vendor/github.com/dsoprea/go-utility/v2/filesystem/write_counter.go
new file mode 100644
index 000000000..dc39901d5
--- /dev/null
+++ b/vendor/github.com/dsoprea/go-utility/v2/filesystem/write_counter.go
@@ -0,0 +1,36 @@
+package rifs
+
+import (
+ "io"
+)
+
+// WriteCounter proxies write requests and maintains a counter of bytes written.
+type WriteCounter struct {
+ w io.Writer
+ counter int
+}
+
+// NewWriteCounter returns a new `WriteCounter` struct wrapping a `Writer`.
+func NewWriteCounter(w io.Writer) *WriteCounter {
+ return &WriteCounter{
+ w: w,
+ }
+}
+
+// Count returns the total number of bytes read.
+func (wc *WriteCounter) Count() int {
+ return wc.counter
+}
+
+// Reset resets the counter to zero.
+func (wc *WriteCounter) Reset() {
+ wc.counter = 0
+}
+
+// Write forwards a write to the underlying `Writer` while bumping the counter.
+func (wc *WriteCounter) Write(b []byte) (n int, err error) {
+ n, err = wc.w.Write(b)
+ wc.counter += n
+
+ return n, err
+}
diff --git a/vendor/github.com/dsoprea/go-utility/image/README.md b/vendor/github.com/dsoprea/go-utility/v2/image/README.md
index 1509ff666..1509ff666 100644
--- a/vendor/github.com/dsoprea/go-utility/image/README.md
+++ b/vendor/github.com/dsoprea/go-utility/v2/image/README.md
diff --git a/vendor/github.com/dsoprea/go-utility/image/media_parser_type.go b/vendor/github.com/dsoprea/go-utility/v2/image/media_parser_type.go
index b7956ea17..8776a1fdd 100644
--- a/vendor/github.com/dsoprea/go-utility/image/media_parser_type.go
+++ b/vendor/github.com/dsoprea/go-utility/v2/image/media_parser_type.go
@@ -3,7 +3,7 @@ package riimage
import (
"io"
- "github.com/dsoprea/go-exif/v2"
+ "github.com/dsoprea/go-exif/v3"
)
// MediaContext is an accessor that knows how to extract specific metadata from