github.com/boki/go-xmp@v1.0.1/models/exif/model.go (about)

     1  // Copyright (c) 2017-2018 Alexander Eichhorn
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // XMP EXIF Mapping for Exif 2.3 metadata CIPA DC-010-2012
    16  //
    17  // Exif Spec
    18  // Exif 2.3 http://www.cipa.jp/std/documents/e/DC-010-2012_E.pdf
    19  // Exif 2.3 http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
    20  // Exif 2.3.1 http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
    21  //
    22  // see https://www.media.mit.edu/pia/Research/deepview/exif.html
    23  // for a very good explanation of tags and ifd's
    24  
    25  // Package exif implements the Exif 2.3.1 metadata standard as defined in CIPA DC-008-2016.
    26  package exif
    27  
    28  import (
    29  	"fmt"
    30  	"strconv"
    31  	"strings"
    32  
    33  	"trimmer.io/go-xmp/models/dc"
    34  	"trimmer.io/go-xmp/models/ps"
    35  	"trimmer.io/go-xmp/models/tiff"
    36  	"trimmer.io/go-xmp/models/xmp_base"
    37  	"trimmer.io/go-xmp/xmp"
    38  )
    39  
    40  var (
    41  	NsExif    *xmp.Namespace    = xmp.NewNamespace("exif", "http://ns.adobe.com/exif/1.0/", NewModel)
    42  	NsExifEX  *xmp.Namespace    = xmp.NewNamespace("exifEX", "http://cipa.jp/exif/1.0/", NewModel)
    43  	NsExifAux *xmp.Namespace    = xmp.NewNamespace("aux", "http://ns.adobe.com/exif/1.0/aux/", NewModel)
    44  	nslist    xmp.NamespaceList = xmp.NamespaceList{NsExif, NsExifEX, NsExifAux}
    45  )
    46  
    47  func init() {
    48  	for _, v := range nslist {
    49  		xmp.Register(v, xmp.ImageMetadata)
    50  	}
    51  }
    52  
    53  func NewModel(name string) xmp.Model {
    54  	switch name {
    55  	case "exif":
    56  		return &ExifInfo{}
    57  	case "exifEX":
    58  		return &ExifEXInfo{}
    59  	case "aux":
    60  		return &ExifAuxInfo{}
    61  	}
    62  	return nil
    63  }
    64  
    65  func MakeModel(d *xmp.Document) (*ExifInfo, error) {
    66  	m, err := d.MakeModel(NsExif)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	x, _ := m.(*ExifInfo)
    71  	return x, nil
    72  }
    73  
    74  func FindModel(d *xmp.Document) *ExifInfo {
    75  	if m := d.FindModel(NsExif); m != nil {
    76  		return m.(*ExifInfo)
    77  	}
    78  	return nil
    79  }
    80  
    81  type ExifInfo struct {
    82  	// Tiff Info part of Exif info
    83  	Artist                    string                `exif:"0x013b" xmp:"tiff:Artist,omit"`
    84  	ArtistXMP                 xmp.StringList        `exif:"-"      xmp:"dc:creator"`
    85  	BitsPerSample             xmp.IntList           `exif:"0x0102" xmp:"tiff:BitsPerSample"` // 3 components
    86  	Compression               tiff.CompressionType  `exif:"0x0103" xmp:"tiff:Compression"`
    87  	Copyright                 string                `exif:"0x8298" xmp:"tiff:Copyright,omit"`
    88  	CopyrightXMP              xmp.AltString         `exif:"-"      xmp:"dc:rights"`
    89  	DateTime                  Date                  `exif:"0x0132" xmp:"tiff:DateTime,omit"`
    90  	DateTimeXMP               xmp.Date              `exif:"-"      xmp:"xmp:ModifyDate"`
    91  	ImageDescription          string                `exif:"0x010e" xmp:"tiff:ImageDescription,omit"`
    92  	ImageDescriptionXMP       xmp.AltString         `exif:"-"      xmp:"dc:description"`
    93  	ImageLength               int                   `exif:"0x0101" xmp:"tiff:ImageLength"`
    94  	ImageWidth                int                   `exif:"0x0100" xmp:"tiff:ImageWidth"` // A. Tags relating to image data structure
    95  	Make                      string                `exif:"0x010f" xmp:"tiff:Make"`
    96  	Model                     string                `exif:"0x0110" xmp:"tiff:Model"`
    97  	Orientation               tiff.OrientationType  `exif:"0x0112" xmp:"tiff:Orientation"`
    98  	PhotometricInterpretation tiff.ColorModel       `exif:"0x0106" xmp:"tiff:PhotometricInterpretation"`
    99  	PlanarConfiguration       tiff.PlanarType       `exif:"0x011c" xmp:"tiff:PlanarConfiguration"`
   100  	PrimaryChromaticities     xmp.RationalArray     `exif:"0x013f" xmp:"tiff:PrimaryChromaticities"` // 6 components
   101  	ReferenceBlackWhite       xmp.RationalArray     `exif:"0x0214" xmp:"tiff:ReferenceBlackWhite"`   // 6 components
   102  	ResolutionUnit            tiff.ResolutionUnit   `exif:"0x0128" xmp:"tiff:ResolutionUnit"`        // 2 = inches, 3 = centimeters
   103  	SamplesPerPixel           int                   `exif:"0x0115" xmp:"tiff:SamplesPerPixel"`
   104  	Software                  string                `exif:"0x0131" xmp:"tiff:Software,omit"`
   105  	SoftwareXMP               xmp.AgentName         `exif:"-"      xmp:"xmp:CreatorTool"`
   106  	TransferFunction          xmp.IntList           `exif:"0x012d" xmp:"tiff:TransferFunction"` // C. Tags relating to image data characteristics
   107  	WhitePoint                xmp.RationalArray     `exif:"0x013e" xmp:"tiff:WhitePoint"`
   108  	XResolution               xmp.Rational          `exif:"0x011a" xmp:"tiff:XResolution"`
   109  	YCbCrCoefficients         xmp.RationalArray     `exif:"0x0211" xmp:"tiff:YCbCrCoefficients"` // 3 components
   110  	YCbCrPositioning          tiff.YCbCrPosition    `exif:"0x0213" xmp:"tiff:YCbCrPositioning"`
   111  	YCbCrSubSampling          tiff.YCbCrSubSampling `exif:"0x0212" xmp:"tiff:YCbCrSubSampling"`
   112  	YResolution               xmp.Rational          `exif:"0x011b" xmp:"tiff:YResolution"`
   113  
   114  	// Exif info
   115  	ExifVersion              string              `exif:"0x9000" xmp:"exif:ExifVersion"`
   116  	FlashpixVersion          string              `exif:"0xa000" xmp:"exif:FlashpixVersion"`
   117  	ColorSpace               ColorSpace          `exif:"0xa001" xmp:"exif:ColorSpace,empty"`
   118  	ComponentsConfiguration  ComponentArray      `exif:"0x9101" xmp:"exif:ComponentsConfiguration"`
   119  	CompressedBitsPerPixel   xmp.Rational        `exif:"0x9102" xmp:"exif:CompressedBitsPerPixel"`
   120  	PixelXDimension          int                 `exif:"0xa002" xmp:"exif:PixelXDimension"`
   121  	PixelYDimension          int                 `exif:"0xa003" xmp:"exif:PixelYDimension"`
   122  	MakerNote                ByteArray           `exif:"0x927c" xmp:"exif:MakerNote,omit"`
   123  	UserComment              xmp.StringArray     `exif:"0x9286" xmp:"exif:UserComment"`
   124  	RelatedSoundFile         string              `exif:"0xa004" xmp:"exif:RelatedSoundFile"`
   125  	DateTimeOriginal         Date                `exif:"0x9003" xmp:"exif:DateTimeOriginal,omit"`
   126  	DateTimeOriginalXMP      xmp.Date            `exif:"-"      xmp:"photoshop:DateCreated"`
   127  	DateTimeDigitized        Date                `exif:"0x9004" xmp:"-"`
   128  	DateTimeDigitizedXMP     xmp.Date            `exif:"-"      xmp:"exif:DateTimeDigitized"`
   129  	SubSecTime               string              `exif:"0x9290" xmp:"exif:SubSecTime,omit"`
   130  	SubSecTimeOriginal       string              `exif:"0x9291" xmp:"exif:SubSecTimeOriginal,omit"`
   131  	SubSecTimeDigitized      string              `exif:"0x9292" xmp:"exif:SubSecTimeDigitized,omit"`
   132  	ExposureTime             xmp.Rational        `exif:"0x829a" xmp:"exif:ExposureTime"`
   133  	FNumber                  xmp.Rational        `exif:"0x829d" xmp:"exif:FNumber"`
   134  	ExposureProgram          ExposureProgram     `exif:"0x8822" xmp:"exif:ExposureProgram,empty"`
   135  	SpectralSensitivity      string              `exif:"0x8824" xmp:"exif:SpectralSensitivity"`
   136  	OECF                     *OECF               `exif:"0x8828" xmp:"exif:OECF"`
   137  	ShutterSpeedValue        xmp.Rational        `exif:"0x9201" xmp:"exif:ShutterSpeedValue"`
   138  	ApertureValue            xmp.Rational        `exif:"0x9202" xmp:"exif:ApertureValue"`
   139  	BrightnessValue          xmp.Rational        `exif:"0x9203" xmp:"exif:BrightnessValue"`
   140  	ExposureBiasValue        xmp.Rational        `exif:"0x9204" xmp:"exif:ExposureBiasValue"`
   141  	MaxApertureValue         xmp.Rational        `exif:"0x9205" xmp:"exif:MaxApertureValue"`
   142  	SubjectDistance          xmp.Rational        `exif:"0x9206" xmp:"exif:SubjectDistance"`
   143  	MeteringMode             MeteringMode        `exif:"0x9207" xmp:"exif:MeteringMode,empty"`
   144  	LightSource              LightSource         `exif:"0x9208" xmp:"exif:LightSource,empty"`
   145  	Flash                    Flash               `exif:"0x9209" xmp:"exif:Flash"`
   146  	FocalLength              xmp.Rational        `exif:"0x920a" xmp:"exif:FocalLength"`
   147  	SubjectArea              xmp.IntList         `exif:"0x9214" xmp:"exif:SubjectArea"`
   148  	FlashEnergy              xmp.Rational        `exif:"0xa20b" xmp:"exif:FlashEnergy"`
   149  	SpatialFrequencyResponse *OECF               `exif:"0xa20c" xmp:"exif:SpatialFrequencyResponse"`
   150  	FocalPlaneXResolution    xmp.Rational        `exif:"0xa20e" xmp:"exif:FocalPlaneXResolution"`
   151  	FocalPlaneYResolution    xmp.Rational        `exif:"0xa20f" xmp:"exif:FocalPlaneYResolution"`
   152  	FocalPlaneResolutionUnit tiff.ResolutionUnit `exif:"0xa210" xmp:"exif:FocalPlaneResolutionUnit,empty"`
   153  	SubjectLocation          xmp.IntList         `exif:"0xa214" xmp:"exif:SubjectLocation"`
   154  	ExposureIndex            xmp.Rational        `exif:"0xa215" xmp:"exif:ExposureIndex"`
   155  	SensingMethod            SensingMode         `exif:"0xa217" xmp:"exif:SensingMethod,empty"`
   156  	FileSource               FileSourceType      `exif:"0xa300" xmp:"exif:FileSource,empty"`
   157  	SceneType                int                 `exif:"0xa301" xmp:"exif:SceneType,empty"`
   158  	CFAPattern               *CFAPattern         `exif:"0xa302" xmp:"exif:CFAPattern"`
   159  	CustomRendered           RenderMode          `exif:"0xa401" xmp:"exif:CustomRendered,empty"`
   160  	ExposureMode             ExposureMode        `exif:"0xa402" xmp:"exif:ExposureMode,empty"`
   161  	WhiteBalance             WhiteBalanceMode    `exif:"0xa403" xmp:"exif:WhiteBalance,empty"`
   162  	DigitalZoomRatio         xmp.Rational        `exif:"0xa404" xmp:"exif:DigitalZoomRatio"`
   163  	FocalLengthIn35mmFilm    int                 `exif:"0xa405" xmp:"exif:FocalLengthIn35mmFilm"`
   164  	SceneCaptureType         SceneCaptureType    `exif:"0xa406" xmp:"exif:SceneCaptureType,empty"`
   165  	GainControl              GainMode            `exif:"0xa407" xmp:"exif:GainControl,empty"`
   166  	Contrast                 ContrastMode        `exif:"0xa408" xmp:"exif:Contrast,empty"`
   167  	Saturation               SaturationMode      `exif:"0xa409" xmp:"exif:Saturation,empty"`
   168  	Sharpness                SharpnessMode       `exif:"0xa40a" xmp:"exif:Sharpness,empty"`
   169  	DeviceSettingDescription DeviceSettings      `exif:"0xa40b" xmp:"exif:DeviceSettingDescription"`
   170  	SubjectDistanceRange     SubjectDistanceMode `exif:"0xa40c" xmp:"exif:SubjectDistanceRange,empty"`
   171  	ImageUniqueID            string              `exif:"0xa420" xmp:"exif:ImageUniqueID"`
   172  	GPSVersionID             string              `exif:"0x0000" xmp:"exif:GPSVersionID"`
   173  	GPSLatitudeRef           string              `exif:"0x0001" xmp:"-"` // N, S
   174  	GPSLatitude              GPSCoord            `exif:"0x0002" xmp:"-"` // 1 float or 3 rational components
   175  	GPSLatitudeCoord         xmp.GPSCoord        `exif:"-"      xmp:"exif:GPSLatitude"`
   176  	GPSLongitudeRef          string              `exif:"0x0003" xmp:"-"` // E, W
   177  	GPSLongitude             GPSCoord            `exif:"0x0004" xmp:"-"` // 1 float or 3 rational components
   178  	GPSLongitudeCoord        xmp.GPSCoord        `exif:"-"      xmp:"exif:GPSLongitude"`
   179  	GPSAltitudeRef           string              `exif:"0x0005" xmp:"exif:GPSAltitudeRef"`
   180  	GPSAltitude              xmp.Rational        `exif:"0x0006" xmp:"exif:GPSAltitude"`
   181  	GPSTimeStamp             xmp.RationalArray   `exif:"0x0007" xmp:"-"` // 3 components: hour/min/sec in UTC
   182  	GPSDateStamp             Date                `exif:"0x001d" xmp:"-"` // YYYY:MM:DD
   183  	GPSTimeStampXMP          xmp.Date            `exif:"-"      xmp:"exif:GPSTimeStamp"`
   184  	GPSSatellites            string              `exif:"0x0008" xmp:"exif:GPSSatellites"`
   185  	GPSStatus                string              `exif:"0x0009" xmp:"exif:GPSStatus"`
   186  	GPSMeasureMode           string              `exif:"0x000a" xmp:"exif:GPSMeasureMode"`
   187  	GPSDOP                   xmp.Rational        `exif:"0x000b" xmp:"exif:GPSDOP"`
   188  	GPSSpeedRef              string              `exif:"0x000c" xmp:"exif:GPSSpeedRef"`
   189  	GPSSpeed                 xmp.Rational        `exif:"0x000d" xmp:"exif:GPSSpeed"`
   190  	GPSTrackRef              string              `exif:"0x000e" xmp:"exif:GPSTrackRef"`
   191  	GPSTrack                 xmp.Rational        `exif:"0x000f" xmp:"exif:GPSTrack"`
   192  	GPSImgDirectionRef       string              `exif:"0x0010" xmp:"exif:GPSImgDirectionRef"`
   193  	GPSImgDirection          xmp.Rational        `exif:"0x0011" xmp:"exif:GPSImgDirection"`
   194  	GPSMapDatum              string              `exif:"0x0012" xmp:"exif:GPSMapDatum"`
   195  	GPSDestLatitudeRef       string              `exif:"0x0013" xmp:"-"` // N, S
   196  	GPSDestLatitude          GPSCoord            `exif:"0x0014" xmp:"-"` // 1 float or 3 rational components
   197  	GPSDestLatitudeCoord     xmp.GPSCoord        `exif:"-"      xmp:"exif:GPSDestLatitude"`
   198  	GPSDestLongitudeRef      string              `exif:"0x0015" xmp:"-"` // E, W
   199  	GPSDestLongitude         GPSCoord            `exif:"0x0016" xmp:"-"` // 1 float or 3 rational components
   200  	GPSDestLongitudeCoord    xmp.GPSCoord        `exif:"-"      xmp:"exif:GPSDestLongitude"`
   201  	GPSDestBearingRef        string              `exif:"0x0017" xmp:"exif:GPSDestBearingRef"`
   202  	GPSDestBearing           xmp.Rational        `exif:"0x0018" xmp:"exif:GPSDestBearing"`
   203  	GPSDestDistanceRef       string              `exif:"0x0019" xmp:"exif:GPSDestDistanceRef"`
   204  	GPSDestDistance          xmp.Rational        `exif:"0x001a" xmp:"exif:GPSDestDistance"`
   205  	GPSProcessingMethod      string              `exif:"0x001b" xmp:"exif:GPSProcessingMethod"`
   206  	GPSAreaInformation       string              `exif:"0x001c" xmp:"exif:GPSAreaInformation"`
   207  	GPSDifferential          int                 `exif:"0x001e" xmp:"exif:GPSDifferential"`
   208  	GPSHPositioningError     xmp.Rational        `exif:"0x001f" xmp:"exif:GPSHPositioningError"`
   209  	NativeDigest             string              `exif:"-"      xmp:"exif:NativeDigest,omit"` // ingore according to spec
   210  
   211  	// replaced by exifEX ExPhotographicSensitivity
   212  	ISOSpeedRatings xmp.IntList `exif:"-" xmp:"exif:ISOSpeedRatings,omit"`
   213  
   214  	// ExifEX namespace
   215  	// ExInteroperabilityIndex     InteropMode       `exif:"0x0001" xmp:"exif:InteroperabilityIndex"`
   216  	// ExInteroperabilityVersion   string            `exif:"0x0002" xmp:"exif:InteroperabilityVersion"`
   217  	ExRelatedImageFileFormat    string            `exif:"0x1000" xmp:"exif:RelatedImageFileFormat"`
   218  	ExRelatedImageWidth         int               `exif:"0x1001" xmp:"exif:RelatedImageWidth"`
   219  	ExRelatedImageLength        int               `exif:"0x1002" xmp:"exif:RelatedImageLength"`
   220  	ExPhotographicSensitivity   int               `exif:"0x8827" xmp:"exif:PhotographicSensitivity"`
   221  	ExSensitivityType           SensitivityType   `exif:"0x8830" xmp:"exif:SensitivityType"`
   222  	ExStandardOutputSensitivity int               `exif:"0x8831" xmp:"exif:StandardOutputSensitivity"`
   223  	ExRecommendedExposureIndex  int               `exif:"0x8832" xmp:"exif:RecommendedExposureIndex"`
   224  	ExISOSpeed                  int               `exif:"0x8833" xmp:"exif:ISOSpeed"`
   225  	ExISOSpeedLatitudeyyy       int               `exif:"0x8834" xmp:"exif:ISOSpeedLatitudeyyy"`
   226  	ExISOSpeedLatitudezzz       int               `exif:"0x8835" xmp:"exif:ISOSpeedLatitudezzz"`
   227  	ExCameraOwnerName           string            `exif:"0xa430" xmp:"exif:CameraOwnerName"`
   228  	ExBodySerialNumber          string            `exif:"0xa431" xmp:"exif:BodySerialNumber"`
   229  	ExLensSpecification         xmp.RationalArray `exif:"0xa432" xmp:"exif:LensSpecification"` // 4 components: min/max [mm] + min/max [f-stop]
   230  	ExLensMake                  string            `exif:"0xa433" xmp:"exif:LensMake"`
   231  	ExLensModel                 string            `exif:"0xa434" xmp:"exif:LensModel"`
   232  	ExLensSerialNumber          string            `exif:"0xa435" xmp:"exif:LensSerialNumber"`
   233  	ExGamma                     xmp.Rational      `exif:"0xa500" xmp:"exif:Gamma"`
   234  
   235  	// non-standard prefixed ExifAux namespace
   236  	AuxApproximateFocusDistance                           xmp.Rational `xmp:"exif:ApproximateFocusDistance"` // 315/10
   237  	AuxDistortionCorrectionAlreadyApplied                 xmp.Bool     `xmp:"exif:DistortionCorrectionAlreadyApplied"`
   238  	AuxSerialNumber                                       string       `xmp:"exif:SerialNumber"`                                       // 2481231346
   239  	AuxLensInfo                                           string       `xmp:"exif:LensInfo"`                                           // 10/1 22/1 0/0 0/0
   240  	AuxLensDistortInfo                                    string       `xmp:"exif:LensDistortInfo"`                                    //
   241  	AuxLens                                               string       `xmp:"exif:Lens"`                                               // EF-S10-22mm f/3.5-4.5 USM
   242  	AuxLensID                                             string       `xmp:"exif:LensID"`                                             // 235
   243  	AuxImageNumber                                        int          `xmp:"exif:ImageNumber"`                                        // 0
   244  	AuxIsMergedHDR                                        xmp.Bool     `xmp:"exif:IsMergedHDR"`                                        //
   245  	AuxIsMergedPanorama                                   xmp.Bool     `xmp:"exif:IsMergedPanorama"`                                   //
   246  	AuxLateralChromaticAberrationCorrectionAlreadyApplied xmp.Bool     `xmp:"exif:LateralChromaticAberrationCorrectionAlreadyApplied"` //
   247  	AuxVignetteCorrectionAlreadyApplied                   xmp.Bool     `xmp:"exif:VignetteCorrectionAlreadyApplied"`                   //
   248  	AuxFlashCompensation                                  xmp.Rational `xmp:"exif:FlashCompensation"`                                  // 0/1
   249  	AuxFirmware                                           string       `xmp:"exif:Firmware"`                                           // 1.2.5
   250  	AuxOwnerName                                          string       `xmp:"exif:OwnerName"`                                          // unknown
   251  	// AuxLensSerialNumber                                   string        `xmp:"exif:LensSerialNumber"`
   252  }
   253  
   254  func (m *ExifInfo) Namespaces() xmp.NamespaceList {
   255  	return xmp.NamespaceList{NsExif}
   256  }
   257  
   258  func (m *ExifInfo) Can(nsName string) bool {
   259  	return nsName == NsExif.GetName()
   260  }
   261  
   262  func (x *ExifInfo) SyncModel(d *xmp.Document) error {
   263  	return nil
   264  }
   265  
   266  func (x *ExifInfo) SyncFromXMP(d *xmp.Document) error {
   267  
   268  	if !x.DateTimeDigitizedXMP.IsZero() {
   269  		x.DateTimeDigitized = Date(x.DateTimeDigitizedXMP.Value())
   270  		x.SubSecTimeDigitized = strconv.Itoa(x.DateTimeDigitizedXMP.Value().Nanosecond())
   271  	}
   272  
   273  	if len(x.ISOSpeedRatings) > 0 {
   274  		x.ExPhotographicSensitivity = x.ISOSpeedRatings[0]
   275  	}
   276  
   277  	// TODO
   278  	// convert GPS from XMP to exif values
   279  
   280  	if m := dc.FindModel(d); m != nil {
   281  		x.ArtistXMP = m.Creator
   282  		x.Artist = strings.Join(m.Creator, ",")
   283  		x.ImageDescriptionXMP = m.Description
   284  		x.ImageDescription = m.Description.Default()
   285  		x.CopyrightXMP = m.Rights
   286  		x.Copyright = m.Rights.Default()
   287  	}
   288  	if base := xmpbase.FindModel(d); base != nil {
   289  		if !base.ModifyDate.IsZero() {
   290  			x.DateTimeXMP = base.ModifyDate
   291  			x.DateTime = Date(base.ModifyDate.Value())
   292  			x.SubSecTime = strconv.Itoa(base.ModifyDate.Value().Nanosecond())
   293  		}
   294  		if base.CreatorTool.IsZero() {
   295  			x.SoftwareXMP = base.CreatorTool
   296  			x.Software = base.CreatorTool.String()
   297  		}
   298  	}
   299  	if tff := tiff.FindModel(d); tff != nil {
   300  		x.BitsPerSample = tff.BitsPerSample
   301  		x.Compression = tff.Compression
   302  		x.ImageLength = tff.ImageLength
   303  		x.ImageWidth = tff.ImageWidth
   304  		x.Make = tff.Make
   305  		x.Model = tff.Model
   306  		x.Orientation = tff.Orientation
   307  		x.PhotometricInterpretation = tff.PhotometricInterpretation
   308  		x.PlanarConfiguration = tff.PlanarConfiguration
   309  		x.PrimaryChromaticities = tff.PrimaryChromaticities
   310  		x.ReferenceBlackWhite = tff.ReferenceBlackWhite
   311  		x.ResolutionUnit = tff.ResolutionUnit
   312  		x.SamplesPerPixel = tff.SamplesPerPixel
   313  		x.TransferFunction = tff.TransferFunction
   314  		x.WhitePoint = tff.WhitePoint
   315  		x.XResolution = tff.XResolution
   316  		x.YCbCrCoefficients = tff.YCbCrCoefficients
   317  		x.YCbCrPositioning = tff.YCbCrPositioning
   318  		x.YCbCrSubSampling = tff.YCbCrSubSampling
   319  		x.YResolution = tff.YResolution
   320  	}
   321  	if m := ps.FindModel(d); m != nil {
   322  		if !m.DateCreated.IsZero() {
   323  			x.DateTimeOriginalXMP = m.DateCreated
   324  			x.DateTimeOriginal = Date(m.DateCreated.Value())
   325  			x.SubSecTimeOriginal = strconv.Itoa(m.DateCreated.Value().Nanosecond())
   326  		}
   327  	}
   328  	return nil
   329  }
   330  
   331  func (x *ExifInfo) SyncToXMP(d *xmp.Document) error {
   332  	// convert dates, text and GPS properties, ignore errors
   333  	if !x.DateTimeOriginal.IsZero() {
   334  		x.DateTimeOriginalXMP, _ = convertDateToXMP(x.DateTimeOriginal, x.SubSecTimeOriginal)
   335  	}
   336  	if !x.DateTimeDigitized.IsZero() {
   337  		x.DateTimeDigitizedXMP, _ = convertDateToXMP(x.DateTimeDigitized, x.SubSecTimeDigitized)
   338  	}
   339  	if !x.DateTime.IsZero() {
   340  		x.DateTimeXMP, _ = convertDateToXMP(x.DateTime, x.SubSecTime)
   341  	}
   342  	if x.Artist != "" {
   343  		x.ArtistXMP = xmp.StringList(strings.Split(x.Artist, ";"))
   344  	}
   345  	if x.ImageDescription != "" && len(x.ImageDescriptionXMP) == 0 {
   346  		x.ImageDescriptionXMP = xmp.NewAltString(x.ImageDescription)
   347  	}
   348  	if x.Copyright != "" && len(x.CopyrightXMP) == 0 {
   349  		x.CopyrightXMP = xmp.NewAltString(x.Copyright)
   350  	}
   351  	if x.Software != "" && x.SoftwareXMP.IsZero() {
   352  		x.SoftwareXMP = xmp.AgentName(x.Software)
   353  	}
   354  
   355  	if len(x.ISOSpeedRatings) > 0 {
   356  		x.ExPhotographicSensitivity = x.ISOSpeedRatings[0]
   357  	}
   358  
   359  	// convert GPS coordinates
   360  	if v, err := convertGPStoXMP(x.GPSLatitude, x.GPSLatitudeRef); err == nil && v != "" {
   361  		x.GPSLatitudeCoord = v
   362  	}
   363  	if v, err := convertGPStoXMP(x.GPSLongitude, x.GPSLongitudeRef); err == nil && v != "" {
   364  		x.GPSLongitudeCoord = v
   365  	}
   366  	if v, err := convertGPStoXMP(x.GPSDestLatitude, x.GPSDestLatitudeRef); err == nil && v != "" {
   367  		x.GPSDestLatitudeCoord = v
   368  	}
   369  	if v, err := convertGPStoXMP(x.GPSDestLongitude, x.GPSDestLongitudeRef); err == nil && v != "" {
   370  		x.GPSDestLongitudeCoord = v
   371  	}
   372  	if !x.GPSDateStamp.IsZero() {
   373  		x.GPSTimeStampXMP = convertGPSTimestamp(x.GPSDateStamp, x.GPSTimeStamp)
   374  	}
   375  	return nil
   376  }
   377  
   378  func (x *ExifInfo) CanTag(tag string) bool {
   379  	_, err := xmp.GetNativeField(x, tag)
   380  	return err == nil
   381  }
   382  
   383  func (x *ExifInfo) GetTag(tag string) (string, error) {
   384  	tag = strings.ToLower(tag)
   385  	if v, err := xmp.GetNativeField(x, tag); err != nil {
   386  		return "", fmt.Errorf("exif: %v", err)
   387  	} else {
   388  		return v, nil
   389  	}
   390  }
   391  
   392  func (x *ExifInfo) SetTag(tag, value string) error {
   393  	tag = strings.ToLower(tag)
   394  	if err := xmp.SetNativeField(x, tag, value); err != nil {
   395  		return fmt.Errorf("exif: %v", err)
   396  	}
   397  	return nil
   398  }
   399  
   400  func (x *ExifInfo) GetLocaleTag(lang string, tag string) (string, error) {
   401  	tag = strings.ToLower(tag)
   402  	if val, err := xmp.GetLocaleField(x, lang, tag); err != nil {
   403  		return "", fmt.Errorf("exif: %v", err)
   404  	} else {
   405  		return val, nil
   406  	}
   407  }
   408  
   409  func (x *ExifInfo) SetLocaleTag(lang string, tag, value string) error {
   410  	tag = strings.ToLower(tag)
   411  	if err := xmp.SetLocaleField(x, lang, tag, value); err != nil {
   412  		return fmt.Errorf("exif: %v", err)
   413  	}
   414  	return nil
   415  }
   416  
   417  type ExifEXInfo struct {
   418  	InteroperabilityIndex     InteropMode       `exif:"0x0001" xmp:"exifEX:InteroperabilityIndex"`
   419  	InteroperabilityVersion   string            `exif:"0x0002" xmp:"exifEX:InteroperabilityVersion"`
   420  	RelatedImageFileFormat    string            `exif:"0x1000" xmp:"exifEX:RelatedImageFileFormat"`
   421  	RelatedImageWidth         int               `exif:"0x1001" xmp:"exifEX:RelatedImageWidth"`
   422  	RelatedImageLength        int               `exif:"0x1002" xmp:"exifEX:RelatedImageLength"`
   423  	PhotographicSensitivity   int               `exif:"0x8827" xmp:"exifEX:PhotographicSensitivity"`
   424  	SensitivityType           SensitivityType   `exif:"0x8830" xmp:"exifEX:SensitivityType"`
   425  	StandardOutputSensitivity int               `exif:"0x8831" xmp:"exifEX:StandardOutputSensitivity"`
   426  	RecommendedExposureIndex  int               `exif:"0x8832" xmp:"exifEX:RecommendedExposureIndex"`
   427  	ISOSpeed                  int               `exif:"0x8833" xmp:"exifEX:ISOSpeed"`
   428  	ISOSpeedLatitudeyyy       int               `exif:"0x8834" xmp:"exifEX:ISOSpeedLatitudeyyy"`
   429  	ISOSpeedLatitudezzz       int               `exif:"0x8835" xmp:"exifEX:ISOSpeedLatitudezzz"`
   430  	CameraOwnerName           string            `exif:"0xa430" xmp:"exifEX:CameraOwnerName"`
   431  	BodySerialNumber          string            `exif:"0xa431" xmp:"exifEX:BodySerialNumber"`
   432  	LensSpecification         xmp.RationalArray `exif:"0xa432" xmp:"exifEX:LensSpecification"` // 4 components: min/max [mm] + min/max [f-stop]
   433  	LensMake                  string            `exif:"0xa433" xmp:"exifEX:LensMake"`
   434  	LensModel                 string            `exif:"0xa434" xmp:"exifEX:LensModel"`
   435  	LensSerialNumber          string            `exif:"0xa435" xmp:"exifEX:LensSerialNumber"`
   436  	Gamma                     xmp.Rational      `exif:"0xa500" xmp:"exifEX:Gamma"`
   437  }
   438  
   439  func (m *ExifEXInfo) Namespaces() xmp.NamespaceList {
   440  	return xmp.NamespaceList{NsExifEX}
   441  }
   442  
   443  func (m *ExifEXInfo) Can(nsName string) bool {
   444  	return nsName == NsExifEX.GetName()
   445  }
   446  
   447  func (x *ExifEXInfo) SyncModel(d *xmp.Document) error {
   448  	return nil
   449  }
   450  
   451  func (x *ExifEXInfo) SyncFromXMP(d *xmp.Document) error {
   452  	return nil
   453  }
   454  
   455  func (x ExifEXInfo) SyncToXMP(d *xmp.Document) error {
   456  	return nil
   457  }
   458  
   459  func (x *ExifEXInfo) CanTag(tag string) bool {
   460  	_, err := xmp.GetNativeField(x, tag)
   461  	return err == nil
   462  }
   463  
   464  func (x *ExifEXInfo) GetTag(tag string) (string, error) {
   465  	if v, err := xmp.GetNativeField(x, tag); err != nil {
   466  		return "", fmt.Errorf("%s: %v", NsExifEX.GetName(), err)
   467  	} else {
   468  		return v, nil
   469  	}
   470  }
   471  
   472  func (x *ExifEXInfo) SetTag(tag, value string) error {
   473  	if err := xmp.SetNativeField(x, tag, value); err != nil {
   474  		return fmt.Errorf("%s: %v", NsExifEX.GetName(), err)
   475  	}
   476  	return nil
   477  }
   478  
   479  // XMP-EXIF Extra Mappings (XMP only)
   480  // The schema namespace URI is http://ns.adobe.com/exif/1.0/aux/
   481  // The preferred schema namespace prefix is aux
   482  //
   483  // Adobe-defined auxiliary EXIF tags.  This namespace existed in the XMP
   484  // specification until it was dropped in 2012, presumably due to the
   485  // introduction of the EXIF 2.3 for XMP specification and the exifEX namespace
   486  // at this time.
   487  type ExifAuxInfo struct {
   488  	ApproximateFocusDistance                           xmp.Rational `xmp:"aux:ApproximateFocusDistance"`                           // 315/10
   489  	DistortionCorrectionAlreadyApplied                 xmp.Bool     `xmp:"aux:DistortionCorrectionAlreadyApplied"`                 //
   490  	SerialNumber                                       string       `xmp:"aux:SerialNumber"`                                       // 2481231346
   491  	LensInfo                                           string       `xmp:"aux:LensInfo"`                                           // 10/1 22/1 0/0 0/0
   492  	LensDistortInfo                                    string       `xmp:"aux:LensDistortInfo"`                                    //
   493  	Lens                                               string       `xmp:"aux:Lens"`                                               // EF-S10-22mm f/3.5-4.5 USM
   494  	LensID                                             string       `xmp:"aux:LensID"`                                             // 235
   495  	LensSerialNumber                                   string       `xmp:"aux:LensSerialNumber"`                                   //
   496  	ImageNumber                                        int          `xmp:"aux:ImageNumber"`                                        // 0
   497  	IsMergedHDR                                        xmp.Bool     `xmp:"aux:IsMergedHDR"`                                        //
   498  	IsMergedPanorama                                   xmp.Bool     `xmp:"aux:IsMergedPanorama"`                                   //
   499  	LateralChromaticAberrationCorrectionAlreadyApplied xmp.Bool     `xmp:"aux:LateralChromaticAberrationCorrectionAlreadyApplied"` //
   500  	VignetteCorrectionAlreadyApplied                   xmp.Bool     `xmp:"aux:VignetteCorrectionAlreadyApplied"`                   //
   501  	FlashCompensation                                  xmp.Rational `xmp:"aux:FlashCompensation"`                                  // 0/1
   502  	Firmware                                           string       `xmp:"aux:Firmware"`                                           // 1.2.5
   503  	OwnerName                                          string       `xmp:"aux:OwnerName"`                                          // unknown
   504  }
   505  
   506  func (m *ExifAuxInfo) Namespaces() xmp.NamespaceList {
   507  	return xmp.NamespaceList{NsExifAux}
   508  }
   509  
   510  func (m *ExifAuxInfo) Can(nsName string) bool {
   511  	return nsName == NsExifAux.GetName()
   512  }
   513  
   514  func (x *ExifAuxInfo) SyncModel(d *xmp.Document) error {
   515  	return nil
   516  }
   517  
   518  func (x *ExifAuxInfo) SyncFromXMP(d *xmp.Document) error {
   519  	return nil
   520  }
   521  
   522  func (x ExifAuxInfo) SyncToXMP(d *xmp.Document) error {
   523  	return nil
   524  }
   525  
   526  func (x *ExifAuxInfo) CanTag(tag string) bool {
   527  	_, err := xmp.GetNativeField(x, tag)
   528  	return err == nil
   529  }
   530  
   531  func (x *ExifAuxInfo) GetTag(tag string) (string, error) {
   532  	if v, err := xmp.GetNativeField(x, tag); err != nil {
   533  		return "", fmt.Errorf("%s: %v", NsExifAux.GetName(), err)
   534  	} else {
   535  		return v, nil
   536  	}
   537  }
   538  
   539  func (x *ExifAuxInfo) SetTag(tag, value string) error {
   540  	if err := xmp.SetNativeField(x, tag, value); err != nil {
   541  		return fmt.Errorf("%s: %v", NsExifAux.GetName(), err)
   542  	}
   543  	return nil
   544  }