...

Source file src/github.com/dsoprea/go-exif/v3/common/type.go

Documentation: github.com/dsoprea/go-exif/v3/common

     1  package exifcommon
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  	"unicode"
    10  
    11  	"encoding/binary"
    12  
    13  	"github.com/dsoprea/go-logging"
    14  )
    15  
    16  var (
    17  	typeLogger = log.NewLogger("exif.type")
    18  )
    19  
    20  var (
    21  	// ErrNotEnoughData is used when there isn't enough data to accommodate what
    22  	// we're trying to parse (sizeof(type) * unit_count).
    23  	ErrNotEnoughData = errors.New("not enough data for type")
    24  
    25  	// ErrWrongType is used when we try to parse anything other than the
    26  	// current type.
    27  	ErrWrongType = errors.New("wrong type, can not parse")
    28  
    29  	// ErrUnhandledUndefinedTypedTag is used when we try to parse a tag that's
    30  	// recorded as an "unknown" type but not a documented tag (therefore
    31  	// leaving us not knowning how to read it).
    32  	ErrUnhandledUndefinedTypedTag = errors.New("not a standard unknown-typed tag")
    33  )
    34  
    35  // TagTypePrimitive is a type-alias that let's us easily lookup type properties.
    36  type TagTypePrimitive uint16
    37  
    38  const (
    39  	// TypeByte describes an encoded list of bytes.
    40  	TypeByte TagTypePrimitive = 1
    41  
    42  	// TypeAscii describes an encoded list of characters that is terminated
    43  	// with a NUL in its encoded form.
    44  	TypeAscii TagTypePrimitive = 2
    45  
    46  	// TypeShort describes an encoded list of shorts.
    47  	TypeShort TagTypePrimitive = 3
    48  
    49  	// TypeLong describes an encoded list of longs.
    50  	TypeLong TagTypePrimitive = 4
    51  
    52  	// TypeRational describes an encoded list of rationals.
    53  	TypeRational TagTypePrimitive = 5
    54  
    55  	// TypeUndefined describes an encoded value that has a complex/non-clearcut
    56  	// interpretation.
    57  	TypeUndefined TagTypePrimitive = 7
    58  
    59  	// We've seen type-8, but have no documentation on it.
    60  
    61  	// TypeSignedLong describes an encoded list of signed longs.
    62  	TypeSignedLong TagTypePrimitive = 9
    63  
    64  	// TypeSignedRational describes an encoded list of signed rationals.
    65  	TypeSignedRational TagTypePrimitive = 10
    66  
    67  	// TypeFloat describes an encoded list of floats
    68  	TypeFloat TagTypePrimitive = 11
    69  
    70  	// TypeDouble describes an encoded list of doubles.
    71  	TypeDouble TagTypePrimitive = 12
    72  
    73  	// TypeAsciiNoNul is just a pseudo-type, for our own purposes.
    74  	TypeAsciiNoNul TagTypePrimitive = 0xf0
    75  )
    76  
    77  // String returns the name of the type
    78  func (typeType TagTypePrimitive) String() string {
    79  	return TypeNames[typeType]
    80  }
    81  
    82  // Size returns the size of one atomic unit of the type.
    83  func (tagType TagTypePrimitive) Size() int {
    84  	switch tagType {
    85  	case TypeByte, TypeAscii, TypeAsciiNoNul:
    86  		return 1
    87  	case TypeShort:
    88  		return 2
    89  	case TypeLong, TypeSignedLong, TypeFloat:
    90  		return 4
    91  	case TypeRational, TypeSignedRational, TypeDouble:
    92  		return 8
    93  	default:
    94  		log.Panicf("can not determine tag-value size for type (%d): [%s]",
    95  			tagType,
    96  			TypeNames[tagType])
    97  		// Never called.
    98  		return 0
    99  	}
   100  }
   101  
   102  // IsValid returns true if tagType is a valid type.
   103  func (tagType TagTypePrimitive) IsValid() bool {
   104  
   105  	// TODO(dustin): Add test
   106  
   107  	return tagType == TypeByte ||
   108  		tagType == TypeAscii ||
   109  		tagType == TypeAsciiNoNul ||
   110  		tagType == TypeShort ||
   111  		tagType == TypeLong ||
   112  		tagType == TypeRational ||
   113  		tagType == TypeSignedLong ||
   114  		tagType == TypeSignedRational ||
   115  		tagType == TypeFloat ||
   116  		tagType == TypeDouble ||
   117  		tagType == TypeUndefined
   118  }
   119  
   120  var (
   121  	// TODO(dustin): Rename TypeNames() to typeNames() and add getter.
   122  	TypeNames = map[TagTypePrimitive]string{
   123  		TypeByte:           "BYTE",
   124  		TypeAscii:          "ASCII",
   125  		TypeShort:          "SHORT",
   126  		TypeLong:           "LONG",
   127  		TypeRational:       "RATIONAL",
   128  		TypeUndefined:      "UNDEFINED",
   129  		TypeSignedLong:     "SLONG",
   130  		TypeSignedRational: "SRATIONAL",
   131  		TypeFloat:          "FLOAT",
   132  		TypeDouble:         "DOUBLE",
   133  
   134  		TypeAsciiNoNul: "_ASCII_NO_NUL",
   135  	}
   136  
   137  	typeNamesR = map[string]TagTypePrimitive{}
   138  )
   139  
   140  // Rational describes an unsigned rational value.
   141  type Rational struct {
   142  	// Numerator is the numerator of the rational value.
   143  	Numerator uint32
   144  
   145  	// Denominator is the numerator of the rational value.
   146  	Denominator uint32
   147  }
   148  
   149  // SignedRational describes a signed rational value.
   150  type SignedRational struct {
   151  	// Numerator is the numerator of the rational value.
   152  	Numerator int32
   153  
   154  	// Denominator is the numerator of the rational value.
   155  	Denominator int32
   156  }
   157  
   158  func isPrintableText(s string) bool {
   159  	for _, c := range s {
   160  		// unicode.IsPrint() returns false for newline characters.
   161  		if c == 0x0d || c == 0x0a {
   162  			continue
   163  		} else if unicode.IsPrint(rune(c)) == false {
   164  			return false
   165  		}
   166  	}
   167  
   168  	return true
   169  }
   170  
   171  // Format returns a stringified value for the given encoding. Automatically
   172  // parses. Automatically calculates count based on type size. This function
   173  // also supports undefined-type values (the ones that we support, anyway) by
   174  // way of the String() method that they all require. We can't be more specific
   175  // because we're a base package and we can't refer to it.
   176  func FormatFromType(value interface{}, justFirst bool) (phrase string, err error) {
   177  	defer func() {
   178  		if state := recover(); state != nil {
   179  			err = log.Wrap(state.(error))
   180  		}
   181  	}()
   182  
   183  	// TODO(dustin): !! Add test
   184  
   185  	switch t := value.(type) {
   186  	case []byte:
   187  		return DumpBytesToString(t), nil
   188  	case string:
   189  		for i, c := range t {
   190  			if c == 0 {
   191  				t = t[:i]
   192  				break
   193  			}
   194  		}
   195  
   196  		if isPrintableText(t) == false {
   197  			phrase = fmt.Sprintf("string with binary data (%d bytes)", len(t))
   198  			return phrase, nil
   199  		}
   200  
   201  		return t, nil
   202  	case []uint16, []uint32, []int32, []float64, []float32:
   203  		val := reflect.ValueOf(t)
   204  
   205  		if val.Len() == 0 {
   206  			return "", nil
   207  		}
   208  
   209  		if justFirst == true {
   210  			var valueSuffix string
   211  			if val.Len() > 1 {
   212  				valueSuffix = "..."
   213  			}
   214  
   215  			return fmt.Sprintf("%v%s", val.Index(0), valueSuffix), nil
   216  		}
   217  
   218  		return fmt.Sprintf("%v", val), nil
   219  	case []Rational:
   220  		if len(t) == 0 {
   221  			return "", nil
   222  		}
   223  
   224  		parts := make([]string, len(t))
   225  		for i, r := range t {
   226  			parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
   227  
   228  			if justFirst == true {
   229  				break
   230  			}
   231  		}
   232  
   233  		if justFirst == true {
   234  			var valueSuffix string
   235  			if len(t) > 1 {
   236  				valueSuffix = "..."
   237  			}
   238  
   239  			return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
   240  		}
   241  
   242  		return fmt.Sprintf("%v", parts), nil
   243  	case []SignedRational:
   244  		if len(t) == 0 {
   245  			return "", nil
   246  		}
   247  
   248  		parts := make([]string, len(t))
   249  		for i, r := range t {
   250  			parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
   251  
   252  			if justFirst == true {
   253  				break
   254  			}
   255  		}
   256  
   257  		if justFirst == true {
   258  			var valueSuffix string
   259  			if len(t) > 1 {
   260  				valueSuffix = "..."
   261  			}
   262  
   263  			return fmt.Sprintf("%v%s", parts[0], valueSuffix), nil
   264  		}
   265  
   266  		return fmt.Sprintf("%v", parts), nil
   267  	case fmt.Stringer:
   268  		s := t.String()
   269  		if isPrintableText(s) == false {
   270  			phrase = fmt.Sprintf("stringable with binary data (%d bytes)", len(s))
   271  			return phrase, nil
   272  		}
   273  
   274  		// An undefined value that is documented (or that we otherwise support).
   275  		return s, nil
   276  	default:
   277  		// Affects only "unknown" values, in general.
   278  		log.Panicf("type can not be formatted into string: %v", reflect.TypeOf(value).Name())
   279  
   280  		// Never called.
   281  		return "", nil
   282  	}
   283  }
   284  
   285  // Format returns a stringified value for the given encoding. Automatically
   286  // parses. Automatically calculates count based on type size.
   287  func FormatFromBytes(rawBytes []byte, tagType TagTypePrimitive, justFirst bool, byteOrder binary.ByteOrder) (phrase string, err error) {
   288  	defer func() {
   289  		if state := recover(); state != nil {
   290  			err = log.Wrap(state.(error))
   291  		}
   292  	}()
   293  
   294  	// TODO(dustin): !! Add test
   295  
   296  	typeSize := tagType.Size()
   297  
   298  	if len(rawBytes)%typeSize != 0 {
   299  		log.Panicf("byte-count (%d) does not align for [%s] type with a size of (%d) bytes", len(rawBytes), TypeNames[tagType], typeSize)
   300  	}
   301  
   302  	// unitCount is the calculated unit-count. This should equal the original
   303  	// value from the tag (pre-resolution).
   304  	unitCount := uint32(len(rawBytes) / typeSize)
   305  
   306  	// Truncate the items if it's not bytes or a string and we just want the first.
   307  
   308  	var value interface{}
   309  
   310  	switch tagType {
   311  	case TypeByte:
   312  		var err error
   313  
   314  		value, err = parser.ParseBytes(rawBytes, unitCount)
   315  		log.PanicIf(err)
   316  	case TypeAscii:
   317  		var err error
   318  
   319  		value, err = parser.ParseAscii(rawBytes, unitCount)
   320  		log.PanicIf(err)
   321  	case TypeAsciiNoNul:
   322  		var err error
   323  
   324  		value, err = parser.ParseAsciiNoNul(rawBytes, unitCount)
   325  		log.PanicIf(err)
   326  	case TypeShort:
   327  		var err error
   328  
   329  		value, err = parser.ParseShorts(rawBytes, unitCount, byteOrder)
   330  		log.PanicIf(err)
   331  	case TypeLong:
   332  		var err error
   333  
   334  		value, err = parser.ParseLongs(rawBytes, unitCount, byteOrder)
   335  		log.PanicIf(err)
   336  	case TypeFloat:
   337  		var err error
   338  
   339  		value, err = parser.ParseFloats(rawBytes, unitCount, byteOrder)
   340  		log.PanicIf(err)
   341  	case TypeDouble:
   342  		var err error
   343  
   344  		value, err = parser.ParseDoubles(rawBytes, unitCount, byteOrder)
   345  		log.PanicIf(err)
   346  	case TypeRational:
   347  		var err error
   348  
   349  		value, err = parser.ParseRationals(rawBytes, unitCount, byteOrder)
   350  		log.PanicIf(err)
   351  	case TypeSignedLong:
   352  		var err error
   353  
   354  		value, err = parser.ParseSignedLongs(rawBytes, unitCount, byteOrder)
   355  		log.PanicIf(err)
   356  	case TypeSignedRational:
   357  		var err error
   358  
   359  		value, err = parser.ParseSignedRationals(rawBytes, unitCount, byteOrder)
   360  		log.PanicIf(err)
   361  	default:
   362  		// Affects only "unknown" values, in general.
   363  		log.Panicf("value of type [%s] can not be formatted into string", tagType.String())
   364  
   365  		// Never called.
   366  		return "", nil
   367  	}
   368  
   369  	phrase, err = FormatFromType(value, justFirst)
   370  	log.PanicIf(err)
   371  
   372  	return phrase, nil
   373  }
   374  
   375  // TranslateStringToType converts user-provided strings to properly-typed
   376  // values. If a string, returns a string. Else, assumes that it's a single
   377  // number. If a list needs to be processed, it is the caller's responsibility to
   378  // split it (according to whichever convention has been established).
   379  func TranslateStringToType(tagType TagTypePrimitive, valueString string) (value interface{}, err error) {
   380  	defer func() {
   381  		if state := recover(); state != nil {
   382  			err = log.Wrap(state.(error))
   383  		}
   384  	}()
   385  
   386  	if tagType == TypeUndefined {
   387  		// The caller should just call String() on the decoded type.
   388  		log.Panicf("undefined-type values are not supported")
   389  	}
   390  
   391  	if tagType == TypeByte {
   392  		wide, err := strconv.ParseInt(valueString, 16, 8)
   393  		log.PanicIf(err)
   394  
   395  		return byte(wide), nil
   396  	} else if tagType == TypeAscii || tagType == TypeAsciiNoNul {
   397  		// Whether or not we're putting an NUL on the end is only relevant for
   398  		// byte-level encoding. This function really just supports a user
   399  		// interface.
   400  
   401  		return valueString, nil
   402  	} else if tagType == TypeShort {
   403  		n, err := strconv.ParseUint(valueString, 10, 16)
   404  		log.PanicIf(err)
   405  
   406  		return uint16(n), nil
   407  	} else if tagType == TypeLong {
   408  		n, err := strconv.ParseUint(valueString, 10, 32)
   409  		log.PanicIf(err)
   410  
   411  		return uint32(n), nil
   412  	} else if tagType == TypeRational {
   413  		parts := strings.SplitN(valueString, "/", 2)
   414  
   415  		numerator, err := strconv.ParseUint(parts[0], 10, 32)
   416  		log.PanicIf(err)
   417  
   418  		denominator, err := strconv.ParseUint(parts[1], 10, 32)
   419  		log.PanicIf(err)
   420  
   421  		return Rational{
   422  			Numerator:   uint32(numerator),
   423  			Denominator: uint32(denominator),
   424  		}, nil
   425  	} else if tagType == TypeSignedLong {
   426  		n, err := strconv.ParseInt(valueString, 10, 32)
   427  		log.PanicIf(err)
   428  
   429  		return int32(n), nil
   430  	} else if tagType == TypeFloat {
   431  		n, err := strconv.ParseFloat(valueString, 32)
   432  		log.PanicIf(err)
   433  
   434  		return float32(n), nil
   435  	} else if tagType == TypeDouble {
   436  		n, err := strconv.ParseFloat(valueString, 64)
   437  		log.PanicIf(err)
   438  
   439  		return float64(n), nil
   440  	} else if tagType == TypeSignedRational {
   441  		parts := strings.SplitN(valueString, "/", 2)
   442  
   443  		numerator, err := strconv.ParseInt(parts[0], 10, 32)
   444  		log.PanicIf(err)
   445  
   446  		denominator, err := strconv.ParseInt(parts[1], 10, 32)
   447  		log.PanicIf(err)
   448  
   449  		return SignedRational{
   450  			Numerator:   int32(numerator),
   451  			Denominator: int32(denominator),
   452  		}, nil
   453  	}
   454  
   455  	log.Panicf("from-string encoding for type not supported; this shouldn't happen: [%s]", tagType.String())
   456  	return nil, nil
   457  }
   458  
   459  // GetTypeByName returns the `TagTypePrimitive` for the given type name.
   460  // Returns (0) if not valid.
   461  func GetTypeByName(typeName string) (tagType TagTypePrimitive, found bool) {
   462  	tagType, found = typeNamesR[typeName]
   463  	return tagType, found
   464  }
   465  
   466  // BasicTag describes a single tag for any purpose.
   467  type BasicTag struct {
   468  	// FqIfdPath is the fully-qualified IFD-path.
   469  	FqIfdPath string
   470  
   471  	// IfdPath is the unindexed IFD-path.
   472  	IfdPath string
   473  
   474  	// TagId is the tag-ID.
   475  	TagId uint16
   476  }
   477  
   478  func init() {
   479  	for typeId, typeName := range TypeNames {
   480  		typeNamesR[typeName] = typeId
   481  	}
   482  }
   483  

View as plain text