Go package to for updating EXIF data in JPEG files.
This is a thin wrapper around the dsoprea's go-exif and go-jpeg-image-structure packages and includes command-line tools for updating the EXIF data JPEG files using key-value parameters as well as a WebAssembly (wasm) binary for updating EXIF data in JavaScript (or other languages that support wasm binaries).
The blog post Updating EXIF metadata in JavaScript (and WebAssembly) includes an example of the WebAssembly binary in action.
As of this writing the majority of EXIF tags are not supported. Currently only EXIF tags of with the following EXIF types are supported: ASCII, BYTE, RATIONAL and SRATIONAL are supported. This is not ideal but I am still trying to get familiar with the requirements of the go-exif package. Contributions and patches for the other remaining EXIF tag types is welcomed.
Error handling removed for the sake of brevity.
package main import ( "flag" "log" "os" "github.com/sfomuseum/go-exif-update" ) func main() { exif_props := map[string]interface{}{ "Artist": "Bob", "Copyright": "SFO Museum", } for _, path := range paths { fh, _ := os.Open(path) defer fh.Close() update.UpdateExif(fh, os.Stdout, exif_props) } } The properties passed to the UpdateExif method are assumed to be suitably typed to be used as input for the exif.SetStandardWithName method. For anything other than simple strings this can become a fairly involved process. I am still trying the understand the go-exif documentation and requirements so any guidance I have to offer remains limited.
This package also provides a convenience PrepareAndUpdateExif method which attempts to translate EXIF property values in to their corresponding go-exif type before invoking the UpdateExif method. For example:
exif_props := map[string]interface{}{ "FNumber": "11/1", // FNumber is stored as a `RATIONAL` value rather than as a string (or `ASCI`) } for _, path := range paths { fh, _ := os.Open(path) defer fh.Close() update.PrepareAndUpdateExif(fh, os.Stdout, exif_props) } The PrepareAndUpdateExif method also supports a limited set of custom non-standard tags. These are provided to simplify the process of assigning multiple or complex EXIF tags.
The list of currently supported non-standard tags is:
| Name | Value | Description | Notes |
|---|---|---|---|
X-Latitude | float64 | Assign a decimal latitude value in to its corresponding GPSLatitude and GPSLatitudeRef properties. | |
X-Longitude | float64 | Assign a decimal longitude value in to its corresponding GPSLongitude and GPSLongitudeRef properties. |
$> make cli GOOS=js GOARCH=wasm go build -mod vendor -o www/wasm/update_exif.wasm cmd/update-exif-wasm/main.go GOOS=js GOARCH=wasm go build -mod vendor -o www/wasm/supported_tags.wasm cmd/tags-supported-wasm/main.go go build -mod vendor -o bin/tags-is-supported cmd/tags-is-supported/main.go go build -mod vendor -o bin/tags-supported cmd/tags-supported/main.go go build -mod vendor -o bin/update-exif cmd/update-exif/main.go go build -mod vendor -o bin/server cmd/update-exif-server/main.go As part of the build process for tools the two WebAssembly (wasm) binaries that are used by the update-exif-server tool are compiled and placee. You can also build the wasm binaries separately using the wasm Makefile target:
$> make wasm GOOS=js GOARCH=wasm go build -mod vendor -o www/wasm/update_exif.wasm cmd/update-exif-wasm/main.go GOOS=js GOARCH=wasm go build -mod vendor -o www/wasm/supported_tags.wasm cmd/tags-supported-wasm/main.go Command-line tool for indicating whether a named EXIF tag is supported by the sfomuseum/go-exif-update package.
$> ./bin/tags-is-supported -h Command-line tool for indicating whether a named EXIF tag is supported by the sfomuseum/go-exif-update package. Usage: ./bin/tags-is-supported tag(N) tag(N) tag(N) For example:
$> ./bin/tags-is-supported Copyright ImageWidth Copyright true ImageWidth false As of this writing non-standard tags, described above, are not considered when processing tags.
Command-line tool that prints a list of EXIF tag names, sorted alphabetically, that are supported by the sfomuseum/go-exif-update package.
$> ./bin/tags-supported -h Command-line tool that prints a list of EXIF tag names, sorted alphabetically, that are supported by the sfomuseum/go-exif-update package. Usage: ./bin/tags-supported As of this writing non-standard tags, described above, are not considered when listing supported tags.
Command-line tool for updating the EXIF properties in one or more JPEG images.
$> ./bin/update-exif -h Command-line tool for updating the EXIF properties in one or more JPEG images. Images are not updated in place but written to STDOUT. Usage: ./bin/update-exif [options] image(N) image(N) image(N) -property value One or more {TAG}={VALUE} EXIF properties. {TAG} must be a recognized EXIF tag. For example:
$> ./bin/update-exif \ -property Artist=Bob \ -property Copyright=Alice \ test1.jpg > test2.jpg And then verifying the data using the exiv2 tool:
$> exiv2 -pa test2.jpg Exif.Image.Artist Ascii 4 Bob Exif.Image.Copyright Ascii 6 Alice This tool uses the PrepareAndUpdateExif method and supports non-standard tags, both described above. For example:
$> ./bin/update-exif \ -property X-Latitude=37.61799 \ -property X-Longitude=-122.384864 \ -property Artist=Walrus \ fixtures/walrus.jpg \ > walrus-exif.jpg $> exiv2 -pa walrus-exif.jpg Exif.Image.GPSTag Long 1 45 Exif.GPSInfo.GPSLongitude Rational 3 122deg 23' 5" Exif.GPSInfo.GPSLongitudeRef Ascii 2 West Exif.GPSInfo.GPSLatitude Rational 3 37deg 37' 4" Exif.GPSInfo.GPSLatitudeRef Ascii 2 North Exif.Image.Artist Ascii 7 Walrus HTTP server for demonstrating the use of the update_exif WebAssembly binary.
$> ./bin/update-exif-wasm-demo -h HTTP server for demonstrating the use of the update_exif WebAssembly binary. Usage: ./bin/update-exif-wasm-demo [options] -bootstrap-prefix string A relative path to append to all Bootstrap-related paths the server will listen for requests on. -server-uri string A valid aaronland/go-http-server URI. (default "http://localhost:8080") For example:
$> ./bin/update-exif-wasm-demo 2021/04/09 17:32:21 Listening on http://localhost:8080 This will start a web server on localhost:8080 hosting a small web application for updating the EXIF tags in a static image. The application is bundled with two WebAssembly binaries:
supported_tags.wasmthat will return a JSON-encoded list of supported EXIF tags.update_exif.wasmthat will update an image encoded in a base64 data URL from a JSON-encoded dictionary of EXIF tags and values.
You should see something like this:
It's early days so this web application lacks any kind of polish.
Once the image has been uploaded it will trigger a download prompt (using eligrey's FileSaver.js. PNG and GIF images will be re-encoded as JPEG images dynamically.
An abbreviated version of the code to use the update_exif.wasm binary in JavaScript looks like this:
var update = { "Artist": "Bob" }; var enc_update = JSON.stringify(update); var img = document.getElementById("image"); var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var b64_img = canvas.toDataURL("image/jpeg", 1.0); update_exif(b64_img, enc_update).then(data_url => { var blob = dataURLToBlob(data_url); saveAs(blob, "example.jpg"); ).catch(err => { console.log("Failed to update EXIF data, ", err); }); For a complete example (which uses the sfomuseum/js-sfomuseum-golang-wasm helper library) consult the www/javascript/init.js file.
The WASM binary included with tool uses the PrepareAndUpdateExif method and supports non-standard tags, both described above.
The following EXIF tags are supported by this package. This list was generated by the tags-supported tool.
AnalogBalance AntiAliasStrength ApertureValue ApertureValue Artist AsShotPreProfileMatrix AsShotProfileName AsShotWhiteXY BaselineExposure BaselineNoise BaselineSharpness BatteryLevel BestQualityScale BlackLevel BlackLevelDeltaH BlackLevelDeltaV BodySerialNumber BrightnessValue BrightnessValue CFAPattern CFAPlaneColor CameraCalibration1 CameraCalibration2 CameraCalibrationSignature CameraOwnerName CameraSerialNumber ChromaBlurRadius ClipPath ColorMatrix1 ColorMatrix2 CompressedBitsPerPixel CompressedBitsPerPixel Copyright CurrentPreProfileMatrix DNGBackwardVersion DNGPrivateData DNGVersion DateTime DateTimeDigitized DateTimeOriginal DateTimeOriginal DefaultScale DigitalZoomRatio DocumentName DotRange ExposureBiasValue ExposureBiasValue ExposureIndex ExposureIndex ExposureTime ExposureTime FNumber FNumber FlashEnergy FlashEnergy FocalLength FocalLength FocalPlaneXResolution FocalPlaneXResolution FocalPlaneYResolution FocalPlaneYResolution ForwardMatrix1 ForwardMatrix2 GPSAltitude GPSAltitudeRef GPSDOP GPSDateStamp GPSDestBearing GPSDestBearingRef GPSDestDistance GPSDestDistanceRef GPSDestLatitude GPSDestLatitudeRef GPSDestLongitude GPSDestLongitudeRef GPSImgDirection GPSImgDirectionRef GPSLatitude GPSLatitudeRef GPSLongitude GPSLongitudeRef GPSMapDatum GPSMeasureMode GPSSatellites GPSSpeed GPSSpeedRef GPSStatus GPSTimeStamp GPSTrack GPSTrackRef GPSVersionID HostComputer ImageDescription ImageHistory ImageID ImageResources ImageUniqueID InkNames InteroperabilityIndex LensInfo LensMake LensModel LensSerialNumber LensSpecification LinearResponseLimit LocalizedCameraModel Make MaxApertureValue MaxApertureValue Model NoiseProfile NoiseReductionApplied OriginalRawFileName PreviewApplicationName PreviewApplicationVersion PreviewDateTime PreviewSettingsDigest PreviewSettingsName PrimaryChromaticities ProcessingSoftware ProfileCalibrationSignature ProfileCopyright ProfileHueSatMapData1 ProfileHueSatMapData2 ProfileLookTableData ProfileName ProfileToneCurve RawDataUniqueID ReductionMatrix1 ReductionMatrix2 ReferenceBlackWhite RelatedImageFileFormat RelatedSoundFile SecurityClassification ShadowScale ShutterSpeedValue ShutterSpeedValue Software SpectralSensitivity SubSecTime SubSecTimeDigitized SubSecTimeOriginal SubjectDistance TIFFEPStandardID TargetPrinter TimeZoneOffset UniqueCameraModel WhitePoint XClipPathUnits XMLPacket XPAuthor XPComment XPKeywords XPSubject XPTitle XResolution YCbCrCoefficients YClipPathUnits YResolution 
