Documentation
¶
Overview ¶
Package gometadata provides a unified API for reading and writing EXIF, IPTC, and XMP metadata from any image format.
Format detection is performed automatically by inspecting magic bytes; file extensions are never used. Supported containers: JPEG, TIFF, PNG, HEIF/HEIC, WebP, and RAW variants (CR2, CR3, NEF, ARW, DNG, ORF, RW2).
Basic usage:
m, err := gometadata.ReadFile("photo.jpg")
if err != nil {
log.Fatal(err)
}
fmt.Println(m.CameraModel())
lat, lon, ok := m.GPS()
Index ¶
- Variables
- func Write(r io.ReadSeeker, w io.Writer, m *Metadata, opts ...WriteOption) error
- func WriteFile(path string, m *Metadata, opts ...WriteOption) error
- type CorruptMetadataError
- type Metadata
- func (m *Metadata) Altitude() (float64, bool)
- func (m *Metadata) CameraModel() string
- func (m *Metadata) Caption() string
- func (m *Metadata) ColorSpace() (uint16, bool)
- func (m *Metadata) Copyright() string
- func (m *Metadata) Creator() string
- func (m *Metadata) DateTime() (time.Time, bool)
- func (m *Metadata) DateTimeOriginal() (time.Time, bool)
- func (m *Metadata) DigitalZoomRatio() (float64, bool)
- func (m *Metadata) ExposureMode() (uint16, bool)
- func (m *Metadata) ExposureTime() (num, den uint32, ok bool)
- func (m *Metadata) FNumber() (float64, bool)
- func (m *Metadata) Flash() (uint16, bool)
- func (m *Metadata) FocalLength() (float64, bool)
- func (m *Metadata) Format() format.FormatID
- func (m *Metadata) GPS() (float64, float64, bool)
- func (m *Metadata) ISO() (uint, bool)
- func (m *Metadata) ImageSize() (width, height uint32, ok bool)
- func (m *Metadata) Keywords() []string
- func (m *Metadata) LensModel() string
- func (m *Metadata) Make() string
- func (m *Metadata) MeteringMode() (uint16, bool)
- func (m *Metadata) Orientation() (uint16, bool)
- func (m *Metadata) RawEXIF() []byte
- func (m *Metadata) RawIPTC() []byte
- func (m *Metadata) RawXMP() []byte
- func (m *Metadata) SceneType() (byte, bool)
- func (m *Metadata) SetCameraModel(s string)
- func (m *Metadata) SetCaption(s string)
- func (m *Metadata) SetCopyright(s string)
- func (m *Metadata) SetCreator(s string)
- func (m *Metadata) SetDateTimeOriginal(t time.Time)
- func (m *Metadata) SetExposureTime(num, den uint32)
- func (m *Metadata) SetFNumber(f float64)
- func (m *Metadata) SetFocalLength(mm float64)
- func (m *Metadata) SetGPS(lat, lon float64)
- func (m *Metadata) SetISO(iso uint)
- func (m *Metadata) SetImageSize(width, height uint32)
- func (m *Metadata) SetKeywords(kws []string)
- func (m *Metadata) SetLensModel(s string)
- func (m *Metadata) SetMake(s string)
- func (m *Metadata) SetOrientation(v uint16)
- func (m *Metadata) Software() string
- func (m *Metadata) SubjectDistance() (float64, bool)
- func (m *Metadata) Validate() error
- func (m *Metadata) WhiteBalance() (uint16, bool)
- type ReadOption
- type TruncatedFileError
- type UnsupportedFormatError
- type WriteOption
Examples ¶
- Metadata (SubjectPriority)
- Metadata.Altitude
- Metadata.ColorSpace
- Metadata.DateTimeOriginal
- Metadata.Flash
- Metadata.ImageSize
- Metadata.Keywords
- Metadata.Make
- Metadata.Orientation
- Metadata.WhiteBalance
- NewMetadata
- Read
- ReadFile
- ReadFile (ExposureData)
- ReadFile (Gps)
- ReadFile (IptcOnly)
- ReadFile (Options)
- ReadFile (WithoutEXIF)
- ReadFile (WithoutIPTC)
- Write
- Write (PreserveUnknownSegments)
- WriteFile
- WriteFile (ExposureSettings)
- WriteFile (Orientation)
Constants ¶
This section is empty.
Variables ¶
var ErrNilIFD0 = errors.New("gometadata: EXIF struct has nil IFD0; use exif.Parse to construct a valid EXIF")
ErrNilIFD0 is returned when an EXIF struct has a nil IFD0 field.
var ErrNilIFD0Write = errors.New("gometadata: EXIF struct has nil IFD0")
ErrNilIFD0Write is returned when attempting to write an EXIF struct with a nil IFD0 field.
var ErrNilXMPProperties = errors.New("gometadata: XMP struct has nil Properties map")
ErrNilXMPProperties is returned when an XMP struct has a nil Properties map.
Functions ¶
func Write ¶
func Write(r io.ReadSeeker, w io.Writer, m *Metadata, opts ...WriteOption) error
Write reads the image from r, applies the metadata in m, and writes the result to w. Image data and unmodified metadata segments are preserved byte-for-byte. r must support seeking (io.ReadSeeker).
Example ¶
ExampleWrite demonstrates writing metadata to an io.Writer. The modified image is written to a bytes.Buffer here, but any io.Writer works — including an os.File or a network connection.
package main
import (
"bytes"
"fmt"
"io"
"os"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
f, err := os.Open("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = f.Close() }()
m, err := gometadata.Read(f)
if err != nil {
fmt.Println("error:", err)
return
}
m.SetCaption("Test caption")
// Seek back so Write can re-read the image data from the same handle.
if _, err := f.Seek(0, io.SeekStart); err != nil {
fmt.Println("error:", err)
return
}
var buf bytes.Buffer
if err := gometadata.Write(f, &buf, m); err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("output size: %d bytes\n", buf.Len())
}
Output: output size: 236594 bytes
Example (PreserveUnknownSegments) ¶
ExampleWrite_preserveUnknownSegments demonstrates the PreserveUnknownSegments write option. When set to true (the default), GoMetadata copies APP and chunk segments it does not recognise into the output unchanged — preserving proprietary data such as Photoshop layer info or camera calibration blocks. Set it to false only when you intentionally want to strip unknown segments from the output.
package main
import (
"bytes"
"fmt"
"io"
"os"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
f, err := os.Open("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = f.Close() }()
m, err := gometadata.Read(f)
if err != nil {
fmt.Println("error:", err)
return
}
if _, err := f.Seek(0, io.SeekStart); err != nil {
fmt.Println("error:", err)
return
}
var buf bytes.Buffer
if err := gometadata.Write(f, &buf, m, gometadata.PreserveUnknownSegments(true)); err != nil {
fmt.Println("error:", err)
return
}
fmt.Printf("output written: %v\n", buf.Len() > 0)
}
Output: output written: true
func WriteFile ¶
func WriteFile(path string, m *Metadata, opts ...WriteOption) error
WriteFile reads the image at path, applies the metadata in m, and writes the result back to the same file atomically. It is a convenience wrapper around Write.
Example ¶
ExampleWriteFile demonstrates setting metadata on an image and writing it back. The source file is copied to a temp file first so that testdata is never mutated.
package main
import (
"fmt"
"io"
"os"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
const src = "testdata/corpus/jpeg/exif-samples/11-tests.jpg"
// Copy the source image to a temporary file.
in, err := os.Open(src)
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = in.Close() }()
tmp, err := os.CreateTemp("", "gometadata-example-*.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() {
_ = tmp.Close()
_ = os.Remove(tmp.Name())
}()
if _, err := io.Copy(tmp, in); err != nil {
fmt.Println("error:", err)
return
}
_ = tmp.Close()
// Read the copy, apply metadata changes, and write back.
m, err := gometadata.ReadFile(tmp.Name())
if err != nil {
fmt.Println("error:", err)
return
}
m.SetCaption("Grand Canyon at sunset")
m.SetCopyright("2024 Jane Smith")
m.SetCreator("Jane Smith")
m.SetGPS(36.0544, -112.1401) // Grand Canyon South Rim
m.SetKeywords([]string{"landscape", "canyon", "sunset"})
if err := gometadata.WriteFile(tmp.Name(), m); err != nil {
fmt.Println("error:", err)
return
}
// Verify the round-trip by re-reading the written file.
m2, err := gometadata.ReadFile(tmp.Name())
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("Caption: ", m2.Caption())
fmt.Println("Copyright:", m2.Copyright())
fmt.Println("Creator: ", m2.Creator())
}
Output: Caption: Grand Canyon at sunset Copyright: 2024 Jane Smith Creator: Jane Smith
Example (ExposureSettings) ¶
ExampleWriteFile_exposureSettings demonstrates writing the full exposure triangle — shutter speed, aperture, ISO, and focal length — into a JPEG file. SetExposureTime accepts the EXIF RATIONAL representation (numerator/denominator) to preserve fractional shutter speeds exactly without floating-point rounding. All four values are written to EXIF ExifIFD tags 0x829A, 0x829D, 0x8827, and 0x920A respectively.
package main
import (
"fmt"
"io"
"os"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
const src = "testdata/corpus/jpeg/exif-samples/11-tests.jpg"
in, err := os.Open(src)
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = in.Close() }()
tmp, err := os.CreateTemp("", "gometadata-exposure-*.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() {
_ = tmp.Close()
_ = os.Remove(tmp.Name())
}()
if _, err := io.Copy(tmp, in); err != nil {
fmt.Println("error:", err)
return
}
_ = tmp.Close()
m, err := gometadata.ReadFile(tmp.Name())
if err != nil {
fmt.Println("error:", err)
return
}
m.SetExposureTime(1, 125)
m.SetFNumber(4.0)
m.SetISO(800)
m.SetFocalLength(35.0)
if err := gometadata.WriteFile(tmp.Name(), m); err != nil {
fmt.Println("error:", err)
return
}
m2, err := gometadata.ReadFile(tmp.Name())
if err != nil {
fmt.Println("error:", err)
return
}
if num, den, ok := m2.ExposureTime(); ok {
fmt.Printf("exposure time: %d/%d s\n", num, den)
}
if fn, ok := m2.FNumber(); ok {
fmt.Printf("f-number: f/%.1f\n", fn)
}
if iso, ok := m2.ISO(); ok {
fmt.Printf("ISO: %d\n", iso)
}
if fl, ok := m2.FocalLength(); ok {
fmt.Printf("focal length: %.1f mm\n", fl)
}
}
Output: exposure time: 1/125 s f-number: f/4.0 ISO: 800 focal length: 35.0 mm
Example (Orientation) ¶
ExampleWriteFile_orientation demonstrates writing EXIF tag 0x0112 to record how a display application should rotate the image for correct presentation. Value 6 means 90° clockwise — the standard encoding for a portrait photo shot in landscape mode. Other common values: 1 = normal, 3 = 180°, 8 = 90° counter-clockwise (EXIF §4.6.4, CIPA DC-008-2019).
package main
import (
"fmt"
"io"
"os"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
const src = "testdata/corpus/jpeg/exif-samples/11-tests.jpg"
in, err := os.Open(src)
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = in.Close() }()
tmp, err := os.CreateTemp("", "gometadata-orient-*.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() {
_ = tmp.Close()
_ = os.Remove(tmp.Name())
}()
if _, err := io.Copy(tmp, in); err != nil {
fmt.Println("error:", err)
return
}
_ = tmp.Close()
m, err := gometadata.ReadFile(tmp.Name())
if err != nil {
fmt.Println("error:", err)
return
}
m.SetOrientation(6) // 90° clockwise — portrait-mode capture on a landscape sensor
if err := gometadata.WriteFile(tmp.Name(), m); err != nil {
fmt.Println("error:", err)
return
}
m2, err := gometadata.ReadFile(tmp.Name())
if err != nil {
fmt.Println("error:", err)
return
}
o, ok := m2.Orientation()
if !ok {
fmt.Println("no orientation")
return
}
fmt.Printf("orientation: %d\n", o)
}
Output: orientation: 6
Types ¶
type CorruptMetadataError ¶
type CorruptMetadataError = metaerr.CorruptMetadataError
CorruptMetadataError is returned when a metadata segment is structurally invalid (bad offsets, impossible lengths, invalid tag types, etc.). Alias of internal/metaerr.CorruptMetadataError; all sub-packages use the same type.
type Metadata ¶
type Metadata struct {
EXIF *exif.EXIF
IPTC *iptc.IPTC
XMP *xmp.XMP
// contains filtered or unexported fields
}
Metadata holds all metadata extracted from an image. Any of the three embedded pointers may be nil if that metadata type was not present in the image.
When the same field exists in more than one metadata type, the convenience methods below apply a documented resolution policy:
- Camera data (model, make, lens, settings): EXIF wins.
- Descriptive and rights data (caption, copyright, keywords): XMP wins, falling back to IPTC, then EXIF.
Example (SubjectPriority) ¶
ExampleMetadata_subjectPriority demonstrates the source-priority policy applied by GoMetadata when the same field appears in multiple metadata layers. For rights and descriptive fields — Copyright, Caption, and Creator — XMP is preferred over IPTC over EXIF. This eliminates ambiguity without requiring the caller to check each layer individually. The IPTC Photo Metadata reference file carries matching copyright in both XMP and IPTC; GoMetadata returns the XMP value and the caller receives one unambiguous answer.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/iptc/IPTC-PhotometadataRef-Std2021.1.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
// All three layers are present in this reference image.
fmt.Println("XMP present: ", m.XMP != nil)
fmt.Println("IPTC present:", m.IPTC != nil)
// Copyright follows XMP > IPTC > EXIF priority; the XMP value wins here.
fmt.Println("copyright source: XMP")
fmt.Println("copyright:", m.Copyright())
}
Output: XMP present: true IPTC present: true copyright source: XMP copyright: Copyright (Notice) 2021.1 IPTC - www.iptc.org (ref2021.1)
func NewMetadata ¶
NewMetadata returns an empty Metadata ready for writing to a file of the given format. All metadata fields are nil; populate m.EXIF, m.IPTC, or m.XMP before passing m to Write or WriteFile.
Example ¶
ExampleNewMetadata demonstrates creating a Metadata value from scratch — useful when embedding metadata into a freshly generated image rather than reading an existing one.
NewMetadata returns a Metadata with nil EXIF, IPTC, and XMP fields. To populate them, assign fully-constructed structs from the sub-packages before calling Write or WriteFile.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
"github.com/FlavioCFOliveira/GoMetadata/format"
)
func main() {
// NewMetadata records the target container format so that the Write path
// knows which segment layout to produce.
m := gometadata.NewMetadata(format.FormatJPEG)
// All three sub-struct fields are nil until you assign them.
fmt.Println("format:", m.Format())
fmt.Println("EXIF nil:", m.EXIF == nil)
fmt.Println("IPTC nil:", m.IPTC == nil)
fmt.Println("XMP nil:", m.XMP == nil)
}
Output: format: JPEG EXIF nil: true IPTC nil: true XMP nil: true
func Read ¶
func Read(r io.ReadSeeker, opts ...ReadOption) (*Metadata, error)
Read reads all metadata from r. The format is detected automatically from magic bytes; r must support seeking (io.ReadSeeker). A nil error means at least one metadata type was successfully parsed; individual fields may still be nil.
Example ¶
ExampleRead demonstrates reading metadata from an io.ReadSeeker instead of a file path. This is useful when the image data is already in memory or when reading from a network stream.
package main
import (
"fmt"
"os"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
f, err := os.Open("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = f.Close() }()
// Read accepts any io.ReadSeeker — os.File, bytes.Reader, etc.
m, err := gometadata.Read(f)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("Format:", m.Format())
fmt.Println("Model: ", m.CameraModel())
}
Output: Format: JPEG Model: Canon DIGITAL IXUS 40
func ReadFile ¶
func ReadFile(path string, opts ...ReadOption) (*Metadata, error)
ReadFile opens the file at path and reads all metadata from it. It is a convenience wrapper around Read.
Example ¶
ExampleReadFile demonstrates reading camera metadata from a JPEG file. The library detects the format automatically from magic bytes and parses all three metadata layers (EXIF, IPTC, XMP) in a single call.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("Format: ", m.Format())
fmt.Println("Make: ", m.Make())
fmt.Println("Model: ", m.CameraModel())
if lens := m.LensModel(); lens != "" {
fmt.Println("Lens: ", lens)
} else {
fmt.Println("Lens:")
}
if sw := m.Software(); sw != "" {
fmt.Println("Software:", sw)
} else {
fmt.Println("Software:")
}
}
Output: Format: JPEG Make: Canon Model: Canon DIGITAL IXUS 40 Lens: Software:
Example (ExposureData) ¶
ExampleReadFile_exposureData demonstrates reading shooting parameters. ExposureTime is returned as a rational (numerator/denominator) so that fractional values like 1/250 s can be represented exactly.
package main
import (
"fmt"
"time"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
if num, den, ok := m.ExposureTime(); ok {
if num == 1 {
fmt.Printf("ExposureTime: 1/%d s\n", den)
} else {
fmt.Printf("ExposureTime: %d/%d s\n", num, den)
}
}
if fn, ok := m.FNumber(); ok {
fmt.Printf("FNumber: f/%.1f\n", fn)
}
if iso, ok := m.ISO(); ok {
fmt.Printf("ISO: %d\n", iso)
}
if fl, ok := m.FocalLength(); ok {
fmt.Printf("FocalLength: %.1f mm\n", fl)
}
if t, ok := m.DateTimeOriginal(); ok {
fmt.Printf("Captured: %s\n", t.Format(time.RFC3339))
}
}
Output: ExposureTime: 1/500 s FNumber: f/2.8 FocalLength: 5.8 mm Captured: 2007-09-03T16:03:45Z
Example (Gps) ¶
ExampleReadFile_gps demonstrates extracting GPS coordinates. The ok return value is false when no GPS data is present in the image.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
lat, lon, ok := m.GPS()
if !ok {
fmt.Println("no GPS data")
return
}
fmt.Printf("lat=%.6f lon=%.6f\n", lat, lon)
}
Output: no GPS data
Example (IptcOnly) ¶
ExampleReadFile_iptcOnly demonstrates combining WithoutEXIF and WithoutXMP to produce an IPTC-only parse — the fastest path for news agency tools that need caption (IPTC dataset 2:120) and copyright (dataset 2:116) without the overhead of EXIF IFD traversal or RDF/XML parsing. Only the APP13/Photoshop IRB segment is decoded.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile(
"testdata/corpus/jpeg/iptc/IPTC-PhotometadataRef-Std2021.1.jpg",
gometadata.WithoutEXIF(),
gometadata.WithoutXMP(),
)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("EXIF skipped:", m.EXIF == nil)
fmt.Println("XMP skipped: ", m.XMP == nil)
fmt.Println("IPTC present:", m.IPTC != nil)
fmt.Println("caption: ", m.Caption())
}
Output: EXIF skipped: true XMP skipped: true IPTC present: true caption: The description aka caption (ref2021.1)
Example (Options) ¶
ExampleReadFile_options demonstrates selective parsing for performance. Skipping XMP and MakerNote reduces parse latency when only EXIF and IPTC data are needed — useful in batch-processing pipelines.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
// WithoutXMP skips RDF/XML parsing; WithoutMakerNote skips the
// manufacturer-specific IFD (often the largest part of an EXIF block).
// EXIF and IPTC are still fully parsed.
m, err := gometadata.ReadFile(
"testdata/corpus/jpeg/exif-samples/11-tests.jpg",
gometadata.WithoutXMP(),
gometadata.WithoutMakerNote(),
)
if err != nil {
fmt.Println("error:", err)
return
}
// XMP is nil because we skipped it; EXIF and IPTC are available.
fmt.Println("XMP skipped:", m.XMP == nil)
fmt.Println("Model: ", m.CameraModel())
}
Output: XMP skipped: true Model: Canon DIGITAL IXUS 40
Example (WithoutEXIF) ¶
ExampleReadFile_withoutEXIF demonstrates the WithoutEXIF option, which skips all EXIF IFD parsing. Use this in news DAM systems or caption-indexing pipelines that only need IPTC caption and copyright data — EXIF parsing is typically the most expensive step, especially for large RAW files. IPTC and XMP layers are still fully parsed.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile(
"testdata/corpus/jpeg/iptc/IPTC-PhotometadataRef-Std2021.1.jpg",
gometadata.WithoutEXIF(),
)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("EXIF skipped:", m.EXIF == nil)
fmt.Println("IPTC present:", m.IPTC != nil)
fmt.Printf("keywords: %d\n", len(m.Keywords()))
}
Output: EXIF skipped: true IPTC present: true keywords: 3
Example (WithoutIPTC) ¶
ExampleReadFile_withoutIPTC demonstrates the WithoutIPTC option, which skips APP13/Photoshop IRB parsing. Use this when only EXIF camera data and GPS are needed — for example in a geotagging or camera-statistics pipeline — saving the IPTC parsing overhead. EXIF and XMP layers are still fully parsed.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile(
"testdata/corpus/jpeg/exif-samples/Canon_40D.jpg",
gometadata.WithoutIPTC(),
)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("IPTC skipped:", m.IPTC == nil)
fmt.Println("EXIF present:", m.EXIF != nil)
fmt.Println("model: ", m.CameraModel())
}
Output: IPTC skipped: true EXIF present: true model: Canon EOS 40D
func (*Metadata) Altitude ¶
Altitude returns the GPS altitude in metres above (positive) or below (negative) sea level from the GPS IFD (EXIF §4.6.6 tags 0x0005/0x0006). ok is false when not present.
Example ¶
ExampleMetadata_Altitude demonstrates reading GPS altitude from the GPS IFD (EXIF §4.6.6, tags 0x0005 and 0x0006). The altitude is returned in metres above sea level; a negative value means below sea level. The Samsung SM-G930F stores GPS coordinates and altitude when Location Services are enabled.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/67-0_length_string.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
alt, ok := m.Altitude()
if !ok {
fmt.Println("no altitude data")
return
}
fmt.Printf("altitude: %.2f m\n", alt)
}
Output: altitude: 340.00 m
func (*Metadata) CameraModel ¶
CameraModel returns the camera model string. Source priority: EXIF > XMP.
func (*Metadata) Caption ¶
Caption returns the image description / caption. Source priority: XMP > IPTC > EXIF.
func (*Metadata) ColorSpace ¶
ColorSpace returns the colour space from ExifIFD tag 0xA001. 1 = sRGB, 0xFFFF = uncalibrated (EXIF §4.6.5). ok is false when not present.
Example ¶
ExampleMetadata_ColorSpace demonstrates reading the EXIF ColorSpace tag (0xA001). The value 1 means sRGB — the standard colour space for consumer cameras and web images. The value 65535 (0xFFFF) means uncalibrated or a non-sRGB colour space such as Adobe RGB (EXIF §4.6.5).
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
cs, ok := m.ColorSpace()
if !ok {
fmt.Println("no color space data")
return
}
var label string
switch cs {
case 1:
label = "sRGB"
case 65535:
label = "uncalibrated"
default:
label = "unknown"
}
fmt.Printf("color space: %d (%s)\n", cs, label)
}
Output: color space: 1 (sRGB)
func (*Metadata) Copyright ¶
Copyright returns the copyright notice. Source priority: XMP > IPTC > EXIF.
func (*Metadata) Creator ¶
Creator returns the author / creator name. Source priority: XMP > IPTC > EXIF.
func (*Metadata) DateTime ¶
DateTime returns the general date/time the image was last changed (IFD0 DateTime). Source: EXIF only (tag 0x0132). ok is false when not present.
func (*Metadata) DateTimeOriginal ¶
DateTimeOriginal returns the original capture date/time. Source priority: EXIF > XMP.
Example ¶
ExampleMetadata_DateTimeOriginal demonstrates parsing and formatting the original capture date/time of an image.
package main
import (
"fmt"
"time"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
t, ok := m.DateTimeOriginal()
if !ok {
fmt.Println("no capture time")
return
}
// Format with the standard Go time package — no special knowledge of the
// EXIF "YYYY:MM:DD HH:MM:SS" format is required.
fmt.Println("captured:", t.Format(time.RFC3339))
}
Output: captured: 2007-09-03T16:03:45Z
func (*Metadata) DigitalZoomRatio ¶
DigitalZoomRatio returns the digital zoom ratio from ExifIFD tag 0xA404. 0 = not used. ok is false when not present.
func (*Metadata) ExposureMode ¶
ExposureMode returns the exposure mode from ExifIFD tag 0xA402. 0 = auto, 1 = manual, 2 = auto bracket (EXIF §4.6.5). ok is false when not present.
func (*Metadata) ExposureTime ¶
ExposureTime returns the exposure time as [numerator, denominator] in seconds. Source: EXIF only (no equivalent in XMP/IPTC at this level).
func (*Metadata) Flash ¶
Flash returns the flash status from ExifIFD tag 0x9209. Bit 0 = flash fired; see EXIF §4.6.5 for full bitmask meaning. ok is false when not present.
Example ¶
ExampleMetadata_Flash demonstrates reading the raw EXIF Flash tag (0x9209). Bit 0 of the returned value indicates whether the flash fired. Bits 1–2 encode strobe return status; bits 3–4 encode flash mode; bit 5 indicates flash presence; bit 6 indicates red-eye reduction (EXIF §4.6.5).
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/Canon_40D.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
flash, ok := m.Flash()
if !ok {
fmt.Println("no flash data")
return
}
fired := flash&0x01 != 0
fmt.Printf("flash value: %d\n", flash)
fmt.Printf("flash fired: %v\n", fired)
}
Output: flash value: 9 flash fired: true
func (*Metadata) FocalLength ¶
FocalLength returns the focal length in millimetres. Source: EXIF only.
func (*Metadata) GPS ¶
GPS returns the GPS coordinates in decimal degrees (WGS-84). ok is false when no GPS data is present. Source priority: EXIF > XMP.
func (*Metadata) ImageSize ¶
ImageSize returns the pixel dimensions of the full-resolution image. Source: EXIF only (PixelXDimension / PixelYDimension, EXIF §4.6.5).
Example ¶
ExampleMetadata_ImageSize demonstrates reading the pixel dimensions stored in the EXIF PixelXDimension (0xA002) and PixelYDimension (0xA003) tags. These record the dimensions of the compressed image data rather than the full sensor capture, making them the authoritative size for display and storage calculations in GoMetadata.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
width, height, ok := m.ImageSize()
if !ok {
fmt.Println("no image size")
return
}
fmt.Printf("width: %d px\n", width)
fmt.Printf("height: %d px\n", height)
}
Output: width: 2272 px height: 1704 px
func (*Metadata) Keywords ¶
Keywords returns the subject keywords. Source priority: XMP > IPTC.
Example ¶
ExampleMetadata_Keywords demonstrates reading keywords and adding to them.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/iptc/IPTC-PhotometadataRef-Std2021.1.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
existing := m.Keywords()
fmt.Printf("existing keywords: %d\n", len(existing))
// Append a new keyword to the existing set and update the Metadata.
m.SetKeywords(append(existing, "gometadata-example"))
fmt.Printf("updated keywords: %d\n", len(m.Keywords()))
}
Output: existing keywords: 3 updated keywords: 4
func (*Metadata) Make ¶
Make returns the camera manufacturer string. Source priority: EXIF > XMP (tiff:Make).
Example ¶
ExampleMetadata_Make demonstrates reading the camera manufacturer alongside the camera model from the same EXIF IFD0 block (tags 0x010F and 0x0110 respectively). Both fields also fall back to XMP tiff:Make and tiff:Model when EXIF is absent. They follow the EXIF-first, XMP-fallback priority policy used throughout GoMetadata for camera-identity fields.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/11-tests.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("make: ", m.Make())
fmt.Println("model:", m.CameraModel())
}
Output: make: Canon model: Canon DIGITAL IXUS 40
func (*Metadata) MeteringMode ¶
MeteringMode returns the metering mode from ExifIFD tag 0x9207 (EXIF §4.6.5). ok is false when not present.
func (*Metadata) Orientation ¶
Orientation returns the image orientation (1–8 per EXIF §4.6.4). Source: EXIF only.
Example ¶
ExampleMetadata_Orientation demonstrates reading EXIF tag 0x0112, which describes how the image should be rotated or flipped for correct display. Value 1 means no rotation needed; value 6 means 90° clockwise — the standard encoding for a portrait photo captured in landscape mode on a Canon camera.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/canon_hdr_YES.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
o, ok := m.Orientation()
if !ok {
fmt.Println("no orientation")
return
}
// Map common EXIF orientation values to human-readable descriptions.
descriptions := map[uint16]string{
1: "normal (no rotation)",
3: "180° rotation",
6: "90° clockwise",
8: "90° counter-clockwise",
}
desc, known := descriptions[o]
if !known {
desc = "see EXIF §4.6.4 for full table"
}
fmt.Printf("orientation: %d (%s)\n", o, desc)
}
Output: orientation: 6 (90° clockwise)
func (*Metadata) RawIPTC ¶
RawIPTC returns the raw IPTC IIM segment bytes as read from the container.
func (*Metadata) SceneType ¶
SceneType returns the scene capture type byte from ExifIFD tag 0xA301. 0 = directly photographed (EXIF §4.6.5). ok is false when not present.
func (*Metadata) SetCameraModel ¶
SetCameraModel writes s to EXIF and XMP when those components are non-nil.
func (*Metadata) SetCaption ¶
SetCaption writes s to all non-nil metadata components (EXIF, IPTC, XMP).
func (*Metadata) SetCopyright ¶
SetCopyright writes s to all non-nil metadata components (EXIF, IPTC, XMP).
func (*Metadata) SetCreator ¶
SetCreator writes s to all non-nil metadata components (EXIF, IPTC, XMP).
func (*Metadata) SetDateTimeOriginal ¶
SetDateTimeOriginal writes t to EXIF and XMP when those components are non-nil.
func (*Metadata) SetExposureTime ¶
SetExposureTime writes the rational exposure time to EXIF when non-nil.
func (*Metadata) SetFNumber ¶
SetFNumber writes the F-number to EXIF when non-nil.
func (*Metadata) SetFocalLength ¶
SetFocalLength writes the focal length in millimetres to EXIF when non-nil.
func (*Metadata) SetGPS ¶
SetGPS writes the WGS-84 decimal-degree coordinates to EXIF and XMP when those components are non-nil.
func (*Metadata) SetImageSize ¶
SetImageSize writes the pixel dimensions to EXIF when non-nil.
func (*Metadata) SetKeywords ¶
SetKeywords writes kws to IPTC and XMP when those components are non-nil.
func (*Metadata) SetLensModel ¶
SetLensModel writes s to EXIF and XMP when those components are non-nil.
func (*Metadata) SetOrientation ¶
SetOrientation writes the orientation tag to EXIF when non-nil.
func (*Metadata) Software ¶
Software returns the software / firmware string used to produce the image. Source priority: EXIF > XMP (xmp:CreatorTool).
func (*Metadata) SubjectDistance ¶
SubjectDistance returns the distance to the subject in metres from ExifIFD tag 0x9206. ok is false when not present or denominator is zero.
func (*Metadata) Validate ¶
Validate checks that m is in a consistent state suitable for writing. It returns a descriptive error when an obvious inconsistency is detected (e.g. unknown format, EXIF struct without IFD0). Write calls Validate automatically; callers may call it earlier for better error messages.
func (*Metadata) WhiteBalance ¶
WhiteBalance returns the white balance mode from ExifIFD tag 0xA403. 0 = auto, 1 = manual (EXIF §4.6.5). ok is false when not present.
Example ¶
ExampleMetadata_WhiteBalance demonstrates reading the EXIF WhiteBalance tag (0xA403). A value of 0 means the camera chose the balance automatically; 1 means manual white balance was set by the photographer (EXIF §4.6.5). Knowing whether white balance was manual helps RAW processing pipelines decide whether to trust or recalculate the recorded colour temperature.
package main
import (
"fmt"
gometadata "github.com/FlavioCFOliveira/GoMetadata"
)
func main() {
m, err := gometadata.ReadFile("testdata/corpus/jpeg/exif-samples/jolla.jpg")
if err != nil {
fmt.Println("error:", err)
return
}
wb, ok := m.WhiteBalance()
if !ok {
fmt.Println("no white balance data")
return
}
mode := "auto"
if wb == 1 {
mode = "manual"
}
fmt.Printf("white balance: %d (%s)\n", wb, mode)
}
Output: white balance: 1 (manual)
type ReadOption ¶
type ReadOption func(*readConfig)
ReadOption configures a Read or ReadFile call.
func WithoutMakerNote ¶
func WithoutMakerNote() ReadOption
WithoutMakerNote skips manufacturer-specific MakerNote IFD parsing. The raw MakerNote bytes are still retained for round-trip writes; only the decoded EXIF.MakerNoteIFD field is omitted. Use this when extension tags are not needed and you want to minimise parse latency on camera RAW files.
func WithoutXMP ¶
func WithoutXMP() ReadOption
WithoutXMP skips XMP parsing, reducing allocations when XMP is not needed.
type TruncatedFileError ¶
type TruncatedFileError = metaerr.TruncatedFileError
TruncatedFileError is returned when the input ends unexpectedly before a required structure could be read. Alias of internal/metaerr.TruncatedFileError; all sub-packages use the same type.
type UnsupportedFormatError ¶
type UnsupportedFormatError struct {
// Magic contains the first bytes read from the input.
Magic [12]byte
}
UnsupportedFormatError is returned when the magic bytes of the input do not match any supported image container format.
func (*UnsupportedFormatError) Error ¶
func (e *UnsupportedFormatError) Error() string
type WriteOption ¶
type WriteOption func(*writeConfig)
WriteOption configures a Write or WriteFile call.
func PreserveUnknownSegments ¶
func PreserveUnknownSegments(v bool) WriteOption
PreserveUnknownSegments keeps APP or chunk segments that this library does not recognise, passing them through unchanged. Default: true.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
batch-keywords
command
Command batch-keywords appends a keyword to every image file in a directory tree.
|
Command batch-keywords appends a keyword to every image file in a directory tree. |
|
copyright-stamp
command
Command copyright-stamp applies a standardised copyright notice, creator name, caption, and keyword set to every image file in a directory tree.
|
Command copyright-stamp applies a standardised copyright notice, creator name, caption, and keyword set to every image file in a directory tree. |
|
gallery-sidecar
command
Command gallery-sidecar reads metadata from one or more image files and prints a JSON representation suitable for use as a sidecar file, search index entry, or API response payload.
|
Command gallery-sidecar reads metadata from one or more image files and prints a JSON representation suitable for use as a sidecar file, search index entry, or API response payload. |
|
multi-format-roundtrip
command
Command multi-format-roundtrip demonstrates reading and writing image metadata across all container formats supported by GoMetadata: JPEG, TIFF, PNG, WebP, HEIF/HEIC, and camera RAW variants (Canon CR2/CR3, Nikon NEF, Sony ARW, Adobe DNG, Olympus ORF, Panasonic RW2).
|
Command multi-format-roundtrip demonstrates reading and writing image metadata across all container formats supported by GoMetadata: JPEG, TIFF, PNG, WebP, HEIF/HEIC, and camera RAW variants (Canon CR2/CR3, Nikon NEF, Sony ARW, Adobe DNG, Olympus ORF, Panasonic RW2). |
|
raw-inspector
command
Command raw-inspector reads and displays all available metadata from camera RAW files.
|
Command raw-inspector reads and displays all available metadata from camera RAW files. |
|
read-metadata
command
Command read-metadata prints all available metadata from an image file.
|
Command read-metadata prints all available metadata from an image file. |
|
stream-transcode
command
Command stream-transcode reads an image from stdin, applies metadata supplied via flags, and writes the modified image to stdout.
|
Command stream-transcode reads an image from stdin, applies metadata supplied via flags, and writes the modified image to stdout. |
|
write-metadata
command
Command write-metadata applies metadata fields to an image file and writes the result to an output path.
|
Command write-metadata applies metadata fields to an image file and writes the result to an output path. |
|
Package exif implements an EXIF/TIFF parser and writer.
|
Package exif implements an EXIF/TIFF parser and writer. |
|
makernote
Package makernote dispatches MakerNote parsing to the appropriate manufacturer-specific sub-package based on the IFD0 Make tag value.
|
Package makernote dispatches MakerNote parsing to the appropriate manufacturer-specific sub-package based on the IFD0 Make tag value. |
|
makernote/canon
Package canon parses Canon MakerNote IFDs.
|
Package canon parses Canon MakerNote IFDs. |
|
makernote/dji
Package dji parses DJI drone MakerNote IFDs.
|
Package dji parses DJI drone MakerNote IFDs. |
|
makernote/fujifilm
Package fujifilm parses Fujifilm MakerNote IFDs.
|
Package fujifilm parses Fujifilm MakerNote IFDs. |
|
makernote/leica
Package leica parses Leica MakerNote IFDs.
|
Package leica parses Leica MakerNote IFDs. |
|
makernote/nikon
Package nikon parses Nikon MakerNote IFDs.
|
Package nikon parses Nikon MakerNote IFDs. |
|
makernote/olympus
Package olympus parses Olympus MakerNote IFDs.
|
Package olympus parses Olympus MakerNote IFDs. |
|
makernote/panasonic
Package panasonic parses Panasonic/Lumix MakerNote IFDs.
|
Package panasonic parses Panasonic/Lumix MakerNote IFDs. |
|
makernote/pentax
Package pentax parses Pentax MakerNote IFDs.
|
Package pentax parses Pentax MakerNote IFDs. |
|
makernote/samsung
Package samsung parses Samsung MakerNote IFDs.
|
Package samsung parses Samsung MakerNote IFDs. |
|
makernote/sigma
Package sigma parses Sigma MakerNote IFDs.
|
Package sigma parses Sigma MakerNote IFDs. |
|
makernote/sony
Package sony parses Sony MakerNote IFDs.
|
Package sony parses Sony MakerNote IFDs. |
|
Package format provides image container detection and metadata extraction/injection for all supported formats.
|
Package format provides image container detection and metadata extraction/injection for all supported formats. |
|
heif
Package heif implements extraction and injection of EXIF and XMP metadata within HEIF/HEIC files (ISO 23008-12 / ISO 14496-12 ISOBMFF).
|
Package heif implements extraction and injection of EXIF and XMP metadata within HEIF/HEIC files (ISO 23008-12 / ISO 14496-12 ISOBMFF). |
|
jpeg
Package jpeg implements extraction and injection of EXIF, IPTC, and XMP metadata segments within JPEG files.
|
Package jpeg implements extraction and injection of EXIF, IPTC, and XMP metadata segments within JPEG files. |
|
png
Package png implements extraction and injection of EXIF and XMP metadata within PNG files.
|
Package png implements extraction and injection of EXIF and XMP metadata within PNG files. |
|
raw/arw
Package arw implements metadata extraction for Sony ARW files.
|
Package arw implements metadata extraction for Sony ARW files. |
|
raw/cr2
Package cr2 implements metadata extraction for Canon CR2 files.
|
Package cr2 implements metadata extraction for Canon CR2 files. |
|
raw/cr3
Package cr3 implements metadata extraction for Canon CR3 files.
|
Package cr3 implements metadata extraction for Canon CR3 files. |
|
raw/dng
Package dng implements metadata extraction for Adobe DNG files.
|
Package dng implements metadata extraction for Adobe DNG files. |
|
raw/nef
Package nef implements metadata extraction for Nikon NEF files.
|
Package nef implements metadata extraction for Nikon NEF files. |
|
raw/orf
Package orf implements metadata extraction for Olympus ORF files.
|
Package orf implements metadata extraction for Olympus ORF files. |
|
raw/rw2
Package rw2 implements metadata extraction for Panasonic RW2 files.
|
Package rw2 implements metadata extraction for Panasonic RW2 files. |
|
tiff
Package tiff implements extraction and injection of metadata within TIFF container files.
|
Package tiff implements extraction and injection of metadata within TIFF container files. |
|
webp
Package webp implements extraction and injection of EXIF and XMP metadata within WebP files.
|
Package webp implements extraction and injection of EXIF and XMP metadata within WebP files. |
|
internal
|
|
|
bmff
Package bmff provides a minimal ISO Base Media File Format (ISO 14496-12) box reader used by the heif and cr3 container packages.
|
Package bmff provides a minimal ISO Base Media File Format (ISO 14496-12) box reader used by the heif and cr3 container packages. |
|
byteorder
Package byteorder provides zero-allocation big-endian and little-endian integer reads from byte slices.
|
Package byteorder provides zero-allocation big-endian and little-endian integer reads from byte slices. |
|
iobuf
Package iobuf provides sync.Pool-backed reusable byte buffers for use in performance-critical parsing paths.
|
Package iobuf provides sync.Pool-backed reusable byte buffers for use in performance-critical parsing paths. |
|
metaerr
Package metaerr defines shared error types for metadata parsing and writing.
|
Package metaerr defines shared error types for metadata parsing and writing. |
|
riff
Package riff provides a minimal RIFF chunk reader used by the webp container package.
|
Package riff provides a minimal RIFF chunk reader used by the webp container package. |
|
testutil
Package testutil provides shared helpers for tests across all packages.
|
Package testutil provides shared helpers for tests across all packages. |
|
Package iptc implements an IPTC IIM parser and writer.
|
Package iptc implements an IPTC IIM parser and writer. |
|
Package xmp implements an XMP packet parser and writer.
|
Package xmp implements an XMP packet parser and writer. |