github.com/boki/go-xmp@v1.0.1/models/exif/types.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  // http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
    16  
    17  package exif
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"math"
    23  	"strconv"
    24  	"strings"
    25  	"time"
    26  
    27  	"trimmer.io/go-xmp/xmp"
    28  )
    29  
    30  const EXIF_DATE_FORMAT = "2006:01:02 15:04:05"
    31  
    32  type ByteArray []byte
    33  
    34  func (x ByteArray) String() string {
    35  	s := make([]string, len(x))
    36  	for i, v := range x {
    37  		s[i] = strconv.Itoa(int(v))
    38  	}
    39  	return strings.Join(s, " ")
    40  }
    41  
    42  func (x ByteArray) MarshalText() ([]byte, error) {
    43  	return []byte(x.String()), nil
    44  }
    45  
    46  func (x *ByteArray) UnmarshalText(data []byte) error {
    47  	fields := strings.Fields(string(data))
    48  	if len(fields) == 0 {
    49  		return nil
    50  	}
    51  	a := make(ByteArray, len(fields))
    52  	for i, v := range fields {
    53  		if j, err := strconv.ParseUint(v, 10, 8); err != nil {
    54  			return fmt.Errorf("exif: invalid byte '%s': %v", v, err)
    55  		} else {
    56  			a[i] = byte(j)
    57  		}
    58  	}
    59  	*x = a
    60  	return nil
    61  }
    62  
    63  type Date time.Time
    64  
    65  func Now() Date {
    66  	return Date(time.Now())
    67  }
    68  
    69  func (x Date) IsZero() bool {
    70  	return time.Time(x).IsZero()
    71  }
    72  
    73  func (x Date) Value() time.Time {
    74  	return time.Time(x)
    75  }
    76  
    77  func (x Date) MarshalText() ([]byte, error) {
    78  	if x.IsZero() {
    79  		return nil, nil
    80  	}
    81  	return []byte(time.Time(x).Format(EXIF_DATE_FORMAT)), nil
    82  }
    83  
    84  func (x Date) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
    85  	return e.EncodeElement(time.Time(x).Format(EXIF_DATE_FORMAT), node)
    86  }
    87  
    88  var timeFormats []string = []string{
    89  	"2006-01-02T15:04:05.999999999",       // XMP
    90  	"2006-01-02T15:04:05.999999999Z07:00", // XMP
    91  	"2006-01-02T15:04:05.999999999Z",      // XMP
    92  	"2006-01-02T15:04:05Z",                // XMP
    93  	"2006-01-02T15:04:05",                 // XMP
    94  	"2006-01-02T15:04",                    // XMP
    95  	"2006:01:02 15:04:05.999999999-07:00", // Exif
    96  	"2006:01:02 15:04:05-07:00",           // Exif
    97  	"2006:01:02 15:04:05",                 // Exif
    98  	"2006:01:02 15:04",                    // Exif
    99  	"2006:01:02",                          // Exif GPS datestamp
   100  	"2006:01",                             // Exif
   101  	"2006",                                // Exif
   102  }
   103  
   104  func (x *Date) UnmarshalText(data []byte) error {
   105  	if len(data) == 0 {
   106  		return nil
   107  	}
   108  	value := string(data)
   109  	for _, f := range timeFormats {
   110  		if t, err := time.Parse(f, value); err == nil {
   111  			*x = Date(t)
   112  			return nil
   113  		}
   114  	}
   115  	return fmt.Errorf("exif: invalid datetime value '%s'", value)
   116  }
   117  
   118  func convertDateToXMP(d Date, s string) (xmp.Date, error) {
   119  	if d.IsZero() {
   120  		return xmp.Date{}, nil
   121  	}
   122  	xd := xmp.Date(d)
   123  	s = strings.TrimSpace(s)
   124  	if s != "" {
   125  		return xd, nil
   126  	}
   127  	if i, err := strconv.ParseInt(s, 10, 64); err != nil {
   128  		return xd, fmt.Errorf("exif: invalid subsecond format '%s': %v", s, err)
   129  	} else {
   130  		sec := d.Value().Unix()
   131  		nsec := i * int64(math.Pow10(9-len(s)))
   132  		xd = xmp.Date(time.Unix(sec, nsec))
   133  	}
   134  	return xd, nil
   135  }
   136  
   137  type ComponentArray []Component
   138  
   139  func (x ComponentArray) String() string {
   140  	s := make([]string, len(x))
   141  	for i, v := range x {
   142  		s[i] = strconv.Itoa(int(v))
   143  	}
   144  	return strings.Join(s, " ")
   145  }
   146  
   147  func (x ComponentArray) Typ() xmp.ArrayType {
   148  	return xmp.ArrayTypeOrdered
   149  }
   150  
   151  func (x ComponentArray) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   152  	return xmp.MarshalArray(e, node, x.Typ(), x)
   153  }
   154  
   155  func (x *ComponentArray) UnmarshalXMP(d *xmp.Decoder, node *xmp.Node, m xmp.Model) error {
   156  	return xmp.UnmarshalArray(d, node, x.Typ(), x)
   157  }
   158  
   159  type OECF struct {
   160  	Columns int               `xmp:"exif:Columns"`
   161  	Rows    int               `xmp:"exif:Rows"`
   162  	Names   xmp.StringArray   `xmp:"exif:Names"`
   163  	Values  xmp.RationalArray `xmp:"exif:Values"`
   164  }
   165  
   166  func (x OECF) IsZero() bool {
   167  	return x.Columns == 0 || x.Rows == 0
   168  }
   169  
   170  func (x *OECF) Addr() *OECF {
   171  	if x.IsZero() {
   172  		return nil
   173  	}
   174  	return x
   175  }
   176  
   177  func (x *OECF) UnmarshalText(data []byte) error {
   178  	var err error
   179  	o := OECF{}
   180  	for i, v := range strings.Fields(string(data)) {
   181  		switch {
   182  		case i == 0:
   183  			if o.Columns, err = strconv.Atoi(v); err != nil {
   184  				return fmt.Errorf("exif: invalid OECF columns value '%s': %v", v, err)
   185  			}
   186  			o.Names = make(xmp.StringArray, 0, o.Columns)
   187  		case i == 1:
   188  			if o.Rows, err = strconv.Atoi(v); err != nil {
   189  				return fmt.Errorf("exif: invalid OECF rows value '%s': %v", v, err)
   190  			}
   191  			o.Values = make(xmp.RationalArray, 0, o.Columns*o.Rows)
   192  		case i > 1 && i < o.Columns+1:
   193  			o.Names = append(o.Names, v)
   194  		default:
   195  			r := xmp.Rational{}
   196  			if err := r.UnmarshalText([]byte(v)); err != nil {
   197  				return fmt.Errorf("exif: invalid OECF value '%s': %v", v, err)
   198  			}
   199  			o.Values = append(o.Values, r)
   200  		}
   201  	}
   202  	*x = o
   203  	return nil
   204  }
   205  
   206  func (x OECF) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   207  	type _t OECF
   208  	return e.EncodeElement(_t(x), node)
   209  }
   210  
   211  type CFAPattern struct {
   212  	Columns int       `xmp:"exif:Columns"`
   213  	Rows    int       `xmp:"exif:Rows"`
   214  	Values  ByteArray `xmp:"exif:Values"`
   215  }
   216  
   217  func (x CFAPattern) IsZero() bool {
   218  	return x.Columns == 0 || x.Rows == 0
   219  }
   220  
   221  func (x *CFAPattern) Addr() *CFAPattern {
   222  	if x.IsZero() {
   223  		return nil
   224  	}
   225  	return x
   226  }
   227  
   228  func (x *CFAPattern) UnmarshalText(data []byte) error {
   229  	var err error
   230  	c := CFAPattern{}
   231  	for i, v := range strings.Fields(string(data)) {
   232  		switch {
   233  		case i == 0:
   234  			if c.Columns, err = strconv.Atoi(v); err != nil {
   235  				return fmt.Errorf("exif: invalid CFA columns value '%s': %v", v, err)
   236  			}
   237  		case i == 1:
   238  			if c.Rows, err = strconv.Atoi(v); err != nil {
   239  				return fmt.Errorf("exif: invalid CFA rows value '%s': %v", v, err)
   240  			}
   241  			c.Values = make(ByteArray, 0, c.Columns*c.Rows)
   242  		default:
   243  			if j, err := strconv.ParseUint(v, 10, 8); err != nil {
   244  				return fmt.Errorf("exif: invalid CFA value '%s': %v", v, err)
   245  			} else {
   246  				c.Values = append(c.Values, byte(j))
   247  			}
   248  		}
   249  	}
   250  	*x = c
   251  	return nil
   252  }
   253  
   254  func (x CFAPattern) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   255  	if x.IsZero() {
   256  		return nil
   257  	}
   258  	type _t CFAPattern
   259  	return e.EncodeElement(_t(x), node)
   260  }
   261  
   262  type DeviceSettings struct {
   263  	Columns int             `xmp:"exif:Columns"`
   264  	Rows    int             `xmp:"exif:Rows"`
   265  	Values  xmp.StringArray `xmp:"exif:Values"`
   266  }
   267  
   268  func (x DeviceSettings) IsZero() bool {
   269  	return x.Columns == 0 || x.Rows == 0
   270  }
   271  
   272  func (x *DeviceSettings) Addr() *DeviceSettings {
   273  	if x.IsZero() {
   274  		return nil
   275  	}
   276  	return x
   277  }
   278  
   279  func (x *DeviceSettings) UnmarshalText(data []byte) error {
   280  	var err error
   281  	s := DeviceSettings{}
   282  	for i, v := range strings.Fields(string(data)) {
   283  		switch {
   284  		case i == 0:
   285  			if s.Columns, err = strconv.Atoi(v); err != nil {
   286  				return fmt.Errorf("exif: invalid device settings columns value '%s': %v", v, err)
   287  			}
   288  		case i == 1:
   289  			if s.Rows, err = strconv.Atoi(v); err != nil {
   290  				return fmt.Errorf("exif: invalid device settings rows value '%s': %v", v, err)
   291  			}
   292  			s.Values = make(xmp.StringArray, 0, s.Columns*s.Rows)
   293  		default:
   294  			s.Values = append(s.Values, v)
   295  		}
   296  	}
   297  	*x = s
   298  	return nil
   299  }
   300  
   301  func (x DeviceSettings) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   302  	if x.IsZero() {
   303  		return nil
   304  	}
   305  	type _t DeviceSettings
   306  	return e.EncodeElement(_t(x), node)
   307  }
   308  
   309  type Flash struct {
   310  	Fired      xmp.Bool        `xmp:"exif:Fired,attr,empty"`
   311  	Function   xmp.Bool        `xmp:"exif:Function,attr,empty"`
   312  	Mode       FlashMode       `xmp:"exif:Mode,attr,empty"`
   313  	RedEyeMode xmp.Bool        `xmp:"exif:RedEyeMode,attr,empty"`
   314  	Return     FlashReturnMode `xmp:"exif:Return,attr,empty"`
   315  }
   316  
   317  func (x Flash) IsZero() bool {
   318  	return x.Mode == 0 && x.Return == 0 && !x.Fired.Value() && !x.Function.Value() && !x.RedEyeMode.Value()
   319  }
   320  
   321  func (x *Flash) UnmarshalText(data []byte) error {
   322  	if len(data) == 0 {
   323  		return nil
   324  	}
   325  	v, err := strconv.ParseInt(string(data), 10, 32)
   326  	if err != nil {
   327  		return fmt.Errorf("exif: invalid flash value '%d': %v", v, err)
   328  	}
   329  	x.Fired = v&0x01 > 0
   330  	x.Return = FlashReturnMode(v >> 1 & 0x3)
   331  	x.Mode = FlashMode(v >> 3 & 0x03)
   332  	x.Function = v&0x20 > 0
   333  	x.RedEyeMode = v&0x40 > 0
   334  	return nil
   335  }
   336  
   337  func (x Flash) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   338  	if x.IsZero() {
   339  		return nil
   340  	}
   341  	type _t Flash
   342  	return e.EncodeElement(_t(x), node)
   343  }
   344  
   345  func (x *Flash) UnmarshalXMP(d *xmp.Decoder, node *xmp.Node, m xmp.Model) error {
   346  	// strip TextUnmarhal method
   347  	type _t Flash
   348  	f := _t{}
   349  	if err := d.DecodeElement(&f, node); err != nil {
   350  		return err
   351  	}
   352  	*x = Flash(f)
   353  	return nil
   354  }
   355  
   356  type GPSCoord [3]xmp.Rational
   357  
   358  // unmarshal from decimal
   359  func (x *GPSCoord) UnmarshalText(data []byte) error {
   360  	s := string(data)
   361  	f, err := strconv.ParseFloat(s, 64)
   362  	if err != nil || s[0] != '-' && len(strings.Split(s, ".")[0]) > 3 {
   363  		return fmt.Errorf("exif: invalid decimal GPS coordinate '%s'", s)
   364  	}
   365  	val := math.Abs(f)
   366  	degrees := int(math.Floor(val))
   367  	minutes := int(math.Floor(60 * (val - float64(degrees))))
   368  	seconds := 3600 * (val - float64(degrees) - (float64(minutes) / 60))
   369  	(*x)[0] = xmp.FloatToRational(float32(degrees))
   370  	(*x)[1] = xmp.FloatToRational(float32(minutes))
   371  	(*x)[2] = xmp.FloatToRational(float32(seconds))
   372  	return nil
   373  }
   374  
   375  func convertGPStoXMP(r GPSCoord, ref string) (xmp.GPSCoord, error) {
   376  	if ref == "" {
   377  		return "", nil
   378  	}
   379  	var deg [3]float64
   380  	for i, v := range r {
   381  		if v.Den == 0 {
   382  			return "", fmt.Errorf("exif: invalid GPS coordinate '%s'", v.String())
   383  		}
   384  		deg[i] = float64(v.Num) / float64(v.Den)
   385  	}
   386  	min := deg[0]*60 + deg[1] + deg[2]/60
   387  	ideg := int(min / 60)
   388  	min -= float64(ideg) * 60
   389  
   390  	buf := bytes.Buffer{}
   391  	buf.Write(strconv.AppendInt(make([]byte, 0, 3), int64(ideg), 10))
   392  	buf.WriteByte(',')
   393  	buf.Write(strconv.AppendFloat(make([]byte, 0, 24), min, 'f', 7, 64))
   394  	buf.WriteString(ref)
   395  	return xmp.GPSCoord(buf.String()), nil
   396  }
   397  
   398  func convertGPSTimestamp(date Date, x xmp.RationalArray) xmp.Date {
   399  	if date.IsZero() {
   400  		return xmp.Date{}
   401  	}
   402  	var d time.Duration
   403  	for i, v := range x {
   404  		switch i {
   405  		case 0:
   406  			d += time.Duration(v.Value() * float64(time.Hour))
   407  		case 1:
   408  			d += time.Duration(v.Value() * float64(time.Minute))
   409  		case 2:
   410  			d += time.Duration(v.Value() * float64(time.Second))
   411  		}
   412  	}
   413  	return xmp.Date(date.Value().Add(d))
   414  }