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
22
23 ErrNotEnoughData = errors.New("not enough data for type")
24
25
26
27 ErrWrongType = errors.New("wrong type, can not parse")
28
29
30
31
32 ErrUnhandledUndefinedTypedTag = errors.New("not a standard unknown-typed tag")
33 )
34
35
36 type TagTypePrimitive uint16
37
38 const (
39
40 TypeByte TagTypePrimitive = 1
41
42
43
44 TypeAscii TagTypePrimitive = 2
45
46
47 TypeShort TagTypePrimitive = 3
48
49
50 TypeLong TagTypePrimitive = 4
51
52
53 TypeRational TagTypePrimitive = 5
54
55
56
57 TypeUndefined TagTypePrimitive = 7
58
59
60
61
62 TypeSignedLong TagTypePrimitive = 9
63
64
65 TypeSignedRational TagTypePrimitive = 10
66
67
68 TypeFloat TagTypePrimitive = 11
69
70
71 TypeDouble TagTypePrimitive = 12
72
73
74 TypeAsciiNoNul TagTypePrimitive = 0xf0
75 )
76
77
78 func (typeType TagTypePrimitive) String() string {
79 return TypeNames[typeType]
80 }
81
82
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
98 return 0
99 }
100 }
101
102
103 func (tagType TagTypePrimitive) IsValid() bool {
104
105
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
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
141 type Rational struct {
142
143 Numerator uint32
144
145
146 Denominator uint32
147 }
148
149
150 type SignedRational struct {
151
152 Numerator int32
153
154
155 Denominator int32
156 }
157
158 func isPrintableText(s string) bool {
159 for _, c := range s {
160
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
172
173
174
175
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
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
275 return s, nil
276 default:
277
278 log.Panicf("type can not be formatted into string: %v", reflect.TypeOf(value).Name())
279
280
281 return "", nil
282 }
283 }
284
285
286
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
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
303
304 unitCount := uint32(len(rawBytes) / typeSize)
305
306
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
363 log.Panicf("value of type [%s] can not be formatted into string", tagType.String())
364
365
366 return "", nil
367 }
368
369 phrase, err = FormatFromType(value, justFirst)
370 log.PanicIf(err)
371
372 return phrase, nil
373 }
374
375
376
377
378
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
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
398
399
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
460
461 func GetTypeByName(typeName string) (tagType TagTypePrimitive, found bool) {
462 tagType, found = typeNamesR[typeName]
463 return tagType, found
464 }
465
466
467 type BasicTag struct {
468
469 FqIfdPath string
470
471
472 IfdPath string
473
474
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