go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/blogctl/pkg/tiff/tag.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package tiff
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/binary"
    13  	"errors"
    14  	"fmt"
    15  	"io"
    16  	"math/big"
    17  	"strings"
    18  	"unicode"
    19  	"unicode/utf8"
    20  )
    21  
    22  // Format specifies the Go type equivalent used to represent the basic
    23  // tiff data types.
    24  type Format int
    25  
    26  // Format constants.
    27  const (
    28  	IntVal Format = iota
    29  	FloatVal
    30  	RatVal
    31  	StringVal
    32  	UndefVal
    33  	OtherVal
    34  )
    35  
    36  // ErrShortReadTagValue is an error constant.
    37  var ErrShortReadTagValue = errors.New("tiff: short read of tag value")
    38  
    39  var formatNames = map[Format]string{
    40  	IntVal:    "int",
    41  	FloatVal:  "float",
    42  	RatVal:    "rational",
    43  	StringVal: "string",
    44  	UndefVal:  "undefined",
    45  	OtherVal:  "other",
    46  }
    47  
    48  // DataType represents the basic tiff tag data types.
    49  type DataType uint16
    50  
    51  // Constants
    52  const (
    53  	DTByte      DataType = 1
    54  	DTAscii     DataType = 2
    55  	DTShort     DataType = 3
    56  	DTLong      DataType = 4
    57  	DTRational  DataType = 5
    58  	DTSByte     DataType = 6
    59  	DTUndefined DataType = 7
    60  	DTSShort    DataType = 8
    61  	DTSLong     DataType = 9
    62  	DTSRational DataType = 10
    63  	DTFloat     DataType = 11
    64  	DTDouble    DataType = 12
    65  )
    66  
    67  var typeNames = map[DataType]string{
    68  	DTByte:      "byte",
    69  	DTAscii:     "ascii",
    70  	DTShort:     "short",
    71  	DTLong:      "long",
    72  	DTRational:  "rational",
    73  	DTSByte:     "signed byte",
    74  	DTUndefined: "undefined",
    75  	DTSShort:    "signed short",
    76  	DTSLong:     "signed long",
    77  	DTSRational: "signed rational",
    78  	DTFloat:     "float",
    79  	DTDouble:    "double",
    80  }
    81  
    82  // typeSize specifies the size in bytes of each type.
    83  var typeSize = map[DataType]uint32{
    84  	DTByte:      1,
    85  	DTAscii:     1,
    86  	DTShort:     2,
    87  	DTLong:      4,
    88  	DTRational:  8,
    89  	DTSByte:     1,
    90  	DTUndefined: 1,
    91  	DTSShort:    2,
    92  	DTSLong:     4,
    93  	DTSRational: 8,
    94  	DTFloat:     4,
    95  	DTDouble:    8,
    96  }
    97  
    98  // Tag reflects the parsed content of a tiff IFD tag.
    99  type Tag struct {
   100  	// Id is the 2-byte tiff tag identifier.
   101  	ID uint16
   102  	// Type is an integer (1 through 12) indicating the tag value's data type.
   103  	Type DataType
   104  	// Count is the number of type Type stored in the tag's value (i.e. the
   105  	// tag's value is an array of type Type and length Count).
   106  	Count uint32
   107  	// Val holds the bytes that represent the tag's value.
   108  	Val []byte
   109  	// ValOffset holds byte offset of the tag value w.r.t. the beginning of the
   110  	// reader it was decoded from. Zero if the tag value fit inside the offset
   111  	// field.
   112  	ValOffset uint32
   113  
   114  	order     binary.ByteOrder
   115  	intVals   []int64
   116  	floatVals []float64
   117  	ratVals   [][]int64
   118  	strVal    string
   119  	format    Format
   120  }
   121  
   122  // DecodeTag parses a tiff-encoded IFD tag from r and returns a Tag object. The
   123  // first read from r should be the first byte of the tag. ReadAt offsets should
   124  // generally be relative to the beginning of the tiff structure (not relative
   125  // to the beginning of the tag).
   126  func DecodeTag(r ReadAtReader, order binary.ByteOrder) (*Tag, error) {
   127  	t := new(Tag)
   128  	t.order = order
   129  
   130  	err := binary.Read(r, order, &t.ID)
   131  	if err != nil {
   132  		return nil, errors.New("tiff: tag id read failed: " + err.Error())
   133  	}
   134  
   135  	err = binary.Read(r, order, &t.Type)
   136  	if err != nil {
   137  		return nil, errors.New("tiff: tag type read failed: " + err.Error())
   138  	}
   139  
   140  	err = binary.Read(r, order, &t.Count)
   141  	if err != nil {
   142  		return nil, errors.New("tiff: tag component count read failed: " + err.Error())
   143  	}
   144  
   145  	// There seems to be a relatively common corrupt tag which has a Count of
   146  	// MaxUint32. This is probably not a valid value, so return early.
   147  	if t.Count == 1<<32-1 {
   148  		return t, errors.New("invalid Count offset in tag")
   149  	}
   150  
   151  	valLen := typeSize[t.Type] * t.Count
   152  	if valLen == 0 {
   153  		return t, errors.New("zero length tag value")
   154  	}
   155  
   156  	if valLen > 4 {
   157  		binary.Read(r, order, &t.ValOffset)
   158  
   159  		// Use a bytes.Buffer so we don't allocate a huge slice if the tag
   160  		// is corrupt.
   161  		var buff bytes.Buffer
   162  		sr := io.NewSectionReader(r, int64(t.ValOffset), int64(valLen))
   163  		n, err := io.Copy(&buff, sr)
   164  		if err != nil {
   165  			return t, errors.New("tiff: tag value read failed: " + err.Error())
   166  		} else if n != int64(valLen) {
   167  			return t, ErrShortReadTagValue
   168  		}
   169  		t.Val = buff.Bytes()
   170  
   171  	} else {
   172  		val := make([]byte, valLen)
   173  		if _, err = io.ReadFull(r, val); err != nil {
   174  			return t, errors.New("tiff: tag offset read failed: " + err.Error())
   175  		}
   176  		// ignore padding.
   177  		if _, err = io.ReadFull(r, make([]byte, 4-valLen)); err != nil {
   178  			return t, errors.New("tiff: tag offset read failed: " + err.Error())
   179  		}
   180  
   181  		t.Val = val
   182  	}
   183  
   184  	return t, t.convertVals()
   185  }
   186  
   187  func (t *Tag) convertVals() error {
   188  	r := bytes.NewReader(t.Val)
   189  
   190  	switch t.Type {
   191  	case DTAscii:
   192  		if len(t.Val) <= 0 {
   193  			break
   194  		}
   195  		nullPos := bytes.IndexByte(t.Val, 0)
   196  		if nullPos == -1 {
   197  			t.strVal = string(t.Val)
   198  		} else {
   199  			// ignore all trailing NULL bytes, in case of a broken t.Count
   200  			t.strVal = string(t.Val[:nullPos])
   201  		}
   202  	case DTByte:
   203  		var v uint8
   204  		t.intVals = make([]int64, int(t.Count))
   205  		for i := range t.intVals {
   206  			err := binary.Read(r, t.order, &v)
   207  			if err != nil {
   208  				return err
   209  			}
   210  			t.intVals[i] = int64(v)
   211  		}
   212  	case DTShort:
   213  		var v uint16
   214  		t.intVals = make([]int64, int(t.Count))
   215  		for i := range t.intVals {
   216  			err := binary.Read(r, t.order, &v)
   217  			if err != nil {
   218  				return err
   219  			}
   220  			t.intVals[i] = int64(v)
   221  		}
   222  	case DTLong:
   223  		var v uint32
   224  		t.intVals = make([]int64, int(t.Count))
   225  		for i := range t.intVals {
   226  			err := binary.Read(r, t.order, &v)
   227  			if err != nil {
   228  				return err
   229  			}
   230  			t.intVals[i] = int64(v)
   231  		}
   232  	case DTSByte:
   233  		var v int8
   234  		t.intVals = make([]int64, int(t.Count))
   235  		for i := range t.intVals {
   236  			err := binary.Read(r, t.order, &v)
   237  			if err != nil {
   238  				return err
   239  			}
   240  			t.intVals[i] = int64(v)
   241  		}
   242  	case DTSShort:
   243  		var v int16
   244  		t.intVals = make([]int64, int(t.Count))
   245  		for i := range t.intVals {
   246  			err := binary.Read(r, t.order, &v)
   247  			if err != nil {
   248  				return err
   249  			}
   250  			t.intVals[i] = int64(v)
   251  		}
   252  	case DTSLong:
   253  		var v int32
   254  		t.intVals = make([]int64, int(t.Count))
   255  		for i := range t.intVals {
   256  			err := binary.Read(r, t.order, &v)
   257  			if err != nil {
   258  				return err
   259  			}
   260  			t.intVals[i] = int64(v)
   261  		}
   262  	case DTRational:
   263  		t.ratVals = make([][]int64, int(t.Count))
   264  		for i := range t.ratVals {
   265  			var n, d uint32
   266  			err := binary.Read(r, t.order, &n)
   267  			if err != nil {
   268  				return err
   269  			}
   270  			err = binary.Read(r, t.order, &d)
   271  			if err != nil {
   272  				return err
   273  			}
   274  			t.ratVals[i] = []int64{int64(n), int64(d)}
   275  		}
   276  	case DTSRational:
   277  		t.ratVals = make([][]int64, int(t.Count))
   278  		for i := range t.ratVals {
   279  			var n, d int32
   280  			err := binary.Read(r, t.order, &n)
   281  			if err != nil {
   282  				return err
   283  			}
   284  			err = binary.Read(r, t.order, &d)
   285  			if err != nil {
   286  				return err
   287  			}
   288  			t.ratVals[i] = []int64{int64(n), int64(d)}
   289  		}
   290  	case DTFloat: // float32
   291  		t.floatVals = make([]float64, int(t.Count))
   292  		for i := range t.floatVals {
   293  			var v float32
   294  			err := binary.Read(r, t.order, &v)
   295  			if err != nil {
   296  				return err
   297  			}
   298  			t.floatVals[i] = float64(v)
   299  		}
   300  	case DTDouble:
   301  		t.floatVals = make([]float64, int(t.Count))
   302  		for i := range t.floatVals {
   303  			var u float64
   304  			err := binary.Read(r, t.order, &u)
   305  			if err != nil {
   306  				return err
   307  			}
   308  			t.floatVals[i] = u
   309  		}
   310  	}
   311  
   312  	switch t.Type {
   313  	case DTByte, DTShort, DTLong, DTSByte, DTSShort, DTSLong:
   314  		t.format = IntVal
   315  	case DTRational, DTSRational:
   316  		t.format = RatVal
   317  	case DTFloat, DTDouble:
   318  		t.format = FloatVal
   319  	case DTAscii:
   320  		t.format = StringVal
   321  	case DTUndefined:
   322  		t.format = UndefVal
   323  	default:
   324  		t.format = OtherVal
   325  	}
   326  
   327  	return nil
   328  }
   329  
   330  // Format returns a value indicating which method can be called to retrieve the
   331  // tag's value properly typed (e.g. integer, rational, etc.).
   332  func (t *Tag) Format() Format { return t.format }
   333  
   334  func (t *Tag) typeErr(to Format) error {
   335  	return &wrongFmtErr{typeNames[t.Type], formatNames[to]}
   336  }
   337  
   338  // Rat returns the tag's i'th value as a rational number. It returns a nil and
   339  // an error if this tag's Format is not RatVal.  It panics for zero deminators
   340  // or if i is out of range.
   341  func (t *Tag) Rat(i int) (*big.Rat, error) {
   342  	n, d, err := t.Rat2(i)
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  	return big.NewRat(n, d), nil
   347  }
   348  
   349  // Rat2 returns the tag's i'th value as a rational number represented by a
   350  // numerator-denominator pair. It returns an error if the tag's Format is not
   351  // RatVal. It panics if i is out of range.
   352  func (t *Tag) Rat2(i int) (num, den int64, err error) {
   353  	if t.format != RatVal {
   354  		return 0, 0, t.typeErr(RatVal)
   355  	}
   356  	return t.ratVals[i][0], t.ratVals[i][1], nil
   357  }
   358  
   359  // Int64 returns the tag's i'th value as an integer. It returns an error if the
   360  // tag's Format is not IntVal. It panics if i is out of range.
   361  func (t *Tag) Int64(i int) (int64, error) {
   362  	if t.format != IntVal {
   363  		return 0, t.typeErr(IntVal)
   364  	}
   365  	return t.intVals[i], nil
   366  }
   367  
   368  // Int returns the tag's i'th value as an integer. It returns an error if the
   369  // tag's Format is not IntVal. It panics if i is out of range.
   370  func (t *Tag) Int(i int) (int, error) {
   371  	if t.format != IntVal {
   372  		return 0, t.typeErr(IntVal)
   373  	}
   374  	return int(t.intVals[i]), nil
   375  }
   376  
   377  // Float returns the tag's i'th value as a float. It returns an error if the
   378  // tag's Format is not IntVal.  It panics if i is out of range.
   379  func (t *Tag) Float(i int) (float64, error) {
   380  	if t.format != FloatVal {
   381  		return 0, t.typeErr(FloatVal)
   382  	}
   383  	return t.floatVals[i], nil
   384  }
   385  
   386  // StringVal returns the tag's value as a string. It returns an error if the
   387  // tag's Format is not StringVal. It panics if i is out of range.
   388  func (t *Tag) StringVal() (string, error) {
   389  	if t.format != StringVal {
   390  		return "", t.typeErr(StringVal)
   391  	}
   392  	return t.strVal, nil
   393  }
   394  
   395  // String returns a nicely formatted version of the tag.
   396  func (t *Tag) String() string {
   397  	data, err := t.MarshalJSON()
   398  	if err != nil {
   399  		return "ERROR: " + err.Error()
   400  	}
   401  
   402  	if t.Count == 1 {
   403  		return strings.Trim(string(data), "[]")
   404  	}
   405  	return string(data)
   406  }
   407  
   408  // MarshalJSON marshals the tag as json.
   409  func (t *Tag) MarshalJSON() ([]byte, error) {
   410  	switch t.format {
   411  	case StringVal, UndefVal:
   412  		return nullString(t.Val), nil
   413  	case OtherVal:
   414  		return []byte(fmt.Sprintf("unknown tag type '%v'", t.Type)), nil
   415  	}
   416  
   417  	rv := []string{}
   418  	for i := 0; i < int(t.Count); i++ {
   419  		switch t.format {
   420  		case RatVal:
   421  			n, d, _ := t.Rat2(i)
   422  			rv = append(rv, fmt.Sprintf(`"%v/%v"`, n, d))
   423  		case FloatVal:
   424  			v, _ := t.Float(i)
   425  			rv = append(rv, fmt.Sprintf("%v", v))
   426  		case IntVal:
   427  			v, _ := t.Int(i)
   428  			rv = append(rv, fmt.Sprintf("%v", v))
   429  		}
   430  	}
   431  	return []byte(fmt.Sprintf(`[%s]`, strings.Join(rv, ","))), nil
   432  }
   433  
   434  func nullString(in []byte) []byte {
   435  	rv := bytes.Buffer{}
   436  	rv.WriteByte('"')
   437  	for _, b := range in {
   438  		if unicode.IsPrint(rune(b)) {
   439  			rv.WriteByte(b)
   440  		}
   441  	}
   442  	rv.WriteByte('"')
   443  	rvb := rv.Bytes()
   444  	if utf8.Valid(rvb) {
   445  		return rvb
   446  	}
   447  	return []byte(`""`)
   448  }
   449  
   450  type wrongFmtErr struct {
   451  	From, To string
   452  }
   453  
   454  func (e *wrongFmtErr) Error() string {
   455  	return fmt.Sprintf("cannot convert tag type '%v' into '%v'", e.From, e.To)
   456  }