github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/resources/images/exif/exif.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package exif
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"io"
    20  	"math/big"
    21  	"regexp"
    22  	"strings"
    23  	"time"
    24  	"unicode"
    25  	"unicode/utf8"
    26  
    27  	"github.com/bep/tmc"
    28  
    29  	_exif "github.com/rwcarlsen/goexif/exif"
    30  	"github.com/rwcarlsen/goexif/tiff"
    31  )
    32  
    33  const exifTimeLayout = "2006:01:02 15:04:05"
    34  
    35  type Exif struct {
    36  	Lat  float64
    37  	Long float64
    38  	Date time.Time
    39  	Tags Tags
    40  }
    41  
    42  type Decoder struct {
    43  	includeFieldsRe  *regexp.Regexp
    44  	excludeFieldsrRe *regexp.Regexp
    45  	noDate           bool
    46  	noLatLong        bool
    47  }
    48  
    49  func IncludeFields(expression string) func(*Decoder) error {
    50  	return func(d *Decoder) error {
    51  		re, err := compileRegexp(expression)
    52  		if err != nil {
    53  			return err
    54  		}
    55  		d.includeFieldsRe = re
    56  		return nil
    57  	}
    58  }
    59  
    60  func ExcludeFields(expression string) func(*Decoder) error {
    61  	return func(d *Decoder) error {
    62  		re, err := compileRegexp(expression)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		d.excludeFieldsrRe = re
    67  		return nil
    68  	}
    69  }
    70  
    71  func WithLatLongDisabled(disabled bool) func(*Decoder) error {
    72  	return func(d *Decoder) error {
    73  		d.noLatLong = disabled
    74  		return nil
    75  	}
    76  }
    77  
    78  func WithDateDisabled(disabled bool) func(*Decoder) error {
    79  	return func(d *Decoder) error {
    80  		d.noDate = disabled
    81  		return nil
    82  	}
    83  }
    84  
    85  func compileRegexp(expression string) (*regexp.Regexp, error) {
    86  	expression = strings.TrimSpace(expression)
    87  	if expression == "" {
    88  		return nil, nil
    89  	}
    90  	if !strings.HasPrefix(expression, "(") {
    91  		// Make it case insensitive
    92  		expression = "(?i)" + expression
    93  	}
    94  
    95  	return regexp.Compile(expression)
    96  }
    97  
    98  func NewDecoder(options ...func(*Decoder) error) (*Decoder, error) {
    99  	d := &Decoder{}
   100  	for _, opt := range options {
   101  		if err := opt(d); err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  
   106  	return d, nil
   107  }
   108  
   109  func (d *Decoder) Decode(r io.Reader) (ex *Exif, err error) {
   110  	defer func() {
   111  		if r := recover(); r != nil {
   112  			err = fmt.Errorf("Exif failed: %v", r)
   113  		}
   114  	}()
   115  
   116  	var x *_exif.Exif
   117  	x, err = _exif.Decode(r)
   118  	if err != nil {
   119  		if err.Error() == "EOF" {
   120  			// Found no Exif
   121  			return nil, nil
   122  		}
   123  		return
   124  	}
   125  
   126  	var tm time.Time
   127  	var lat, long float64
   128  
   129  	if !d.noDate {
   130  		tm, _ = x.DateTime()
   131  	}
   132  
   133  	if !d.noLatLong {
   134  		lat, long, _ = x.LatLong()
   135  	}
   136  
   137  	walker := &exifWalker{x: x, vals: make(map[string]interface{}), includeMatcher: d.includeFieldsRe, excludeMatcher: d.excludeFieldsrRe}
   138  	if err = x.Walk(walker); err != nil {
   139  		return
   140  	}
   141  
   142  	ex = &Exif{Lat: lat, Long: long, Date: tm, Tags: walker.vals}
   143  
   144  	return
   145  }
   146  
   147  func decodeTag(x *_exif.Exif, f _exif.FieldName, t *tiff.Tag) (interface{}, error) {
   148  	switch t.Format() {
   149  	case tiff.StringVal, tiff.UndefVal:
   150  		s := nullString(t.Val)
   151  		if strings.Contains(string(f), "DateTime") {
   152  			if d, err := tryParseDate(x, s); err == nil {
   153  				return d, nil
   154  			}
   155  		}
   156  		return s, nil
   157  	case tiff.OtherVal:
   158  		return "unknown", nil
   159  	}
   160  
   161  	var rv []interface{}
   162  
   163  	for i := 0; i < int(t.Count); i++ {
   164  		switch t.Format() {
   165  		case tiff.RatVal:
   166  			n, d, _ := t.Rat2(i)
   167  			rat := big.NewRat(n, d)
   168  			if n == 1 {
   169  				rv = append(rv, rat)
   170  			} else {
   171  				f, _ := rat.Float64()
   172  				rv = append(rv, f)
   173  			}
   174  
   175  		case tiff.FloatVal:
   176  			v, _ := t.Float(i)
   177  			rv = append(rv, v)
   178  		case tiff.IntVal:
   179  			v, _ := t.Int(i)
   180  			rv = append(rv, v)
   181  		}
   182  	}
   183  
   184  	if t.Count == 1 {
   185  		if len(rv) == 1 {
   186  			return rv[0], nil
   187  		}
   188  	}
   189  
   190  	return rv, nil
   191  }
   192  
   193  // Code borrowed from exif.DateTime and adjusted.
   194  func tryParseDate(x *_exif.Exif, s string) (time.Time, error) {
   195  	dateStr := strings.TrimRight(s, "\x00")
   196  	// TODO(bep): look for timezone offset, GPS time, etc.
   197  	timeZone := time.Local
   198  	if tz, _ := x.TimeZone(); tz != nil {
   199  		timeZone = tz
   200  	}
   201  	return time.ParseInLocation(exifTimeLayout, dateStr, timeZone)
   202  }
   203  
   204  type exifWalker struct {
   205  	x              *_exif.Exif
   206  	vals           map[string]interface{}
   207  	includeMatcher *regexp.Regexp
   208  	excludeMatcher *regexp.Regexp
   209  }
   210  
   211  func (e *exifWalker) Walk(f _exif.FieldName, tag *tiff.Tag) error {
   212  	name := string(f)
   213  	if e.excludeMatcher != nil && e.excludeMatcher.MatchString(name) {
   214  		return nil
   215  	}
   216  	if e.includeMatcher != nil && !e.includeMatcher.MatchString(name) {
   217  		return nil
   218  	}
   219  	val, err := decodeTag(e.x, f, tag)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	e.vals[name] = val
   224  	return nil
   225  }
   226  
   227  func nullString(in []byte) string {
   228  	var rv bytes.Buffer
   229  	for len(in) > 0 {
   230  		r, size := utf8.DecodeRune(in)
   231  		if unicode.IsGraphic(r) {
   232  			rv.WriteRune(r)
   233  		}
   234  		in = in[size:]
   235  	}
   236  	return rv.String()
   237  }
   238  
   239  var tcodec *tmc.Codec
   240  
   241  func init() {
   242  	var err error
   243  	tcodec, err = tmc.New()
   244  	if err != nil {
   245  		panic(err)
   246  	}
   247  }
   248  
   249  type Tags map[string]interface{}
   250  
   251  func (v *Tags) UnmarshalJSON(b []byte) error {
   252  	vv := make(map[string]interface{})
   253  	if err := tcodec.Unmarshal(b, &vv); err != nil {
   254  		return err
   255  	}
   256  
   257  	*v = vv
   258  
   259  	return nil
   260  }
   261  
   262  func (v Tags) MarshalJSON() ([]byte, error) {
   263  	return tcodec.Marshal(v)
   264  }