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 }