forked from google/tiff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfield.go
337 lines (307 loc) · 7.88 KB
/
field.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tiff
// A Field is primarily comprised of an Entry. If the Entry's value is actually
// an offset to the data that the entry describes, then the Field will contain
// both the offset and the data that the offset points to in the file.
import (
"encoding/binary"
"encoding/json"
"fmt"
"math"
"math/big"
"reflect"
"sync"
)
type FieldParser func(BReader, TagSpace, FieldTypeSpace) (Field, error)
var (
tiffFieldPrintFullFieldValue bool
printMu sync.RWMutex
)
func SetTiffFieldPrintFullFieldValue(b bool) {
printMu.Lock()
defer printMu.Unlock()
tiffFieldPrintFullFieldValue = b
}
func GetTiffFieldPrintFullFieldValue() bool {
printMu.RLock()
defer printMu.RUnlock()
return tiffFieldPrintFullFieldValue
}
type FieldValue interface {
Order() binary.ByteOrder
Bytes() []byte
}
type fieldValue struct {
order binary.ByteOrder
value []byte
}
func (fv *fieldValue) Order() binary.ByteOrder {
return fv.order
}
func (fv *fieldValue) Bytes() []byte {
return fv.value
}
func (fv *fieldValue) MarshalJSON() ([]byte, error) {
tmp := struct {
Bytes []byte
}{
Bytes: fv.value,
}
return json.Marshal(tmp)
}
// Field represents a field in an IFD in a TIFF file.
type Field interface {
Tag() Tag
Type() FieldType
Count() uint64
Offset() uint64
Value() FieldValue // TODO: Change to BReader??
ParsedValue() interface{}
}
type field struct {
entry Entry
// If the offset from entry is actually a value, then the bytes will be
// stored here and offset should be considered 0. Otherwise, the offset
// will indicate the location in the file where the values can be found.
// Then value will be used to hold the bytes associated with those values.
value FieldValue
// ftsp is the FieldTypeSpace that can be used to look up the FieldType
// that corresponds to the typeId of this entry. If ftsp is nil, the
// default set DefaultFieldTypeSpace is used instead.
ftsp FieldTypeSpace
// tsp is the TagSpace that can be used to look up the Tag that
// corresponds to the result of entry.TagID().
tsp TagSpace
}
func (f *field) Tag() Tag {
if f.tsp == nil {
return DefaultTagSpace.GetTag(f.entry.TagID())
}
return f.tsp.GetTag(f.entry.TagID())
}
func (f *field) Type() FieldType {
if f.ftsp == nil {
return DefaultFieldTypeSpace.GetFieldType(f.entry.TypeID())
}
return f.ftsp.GetFieldType(f.entry.TypeID())
}
func (f *field) Count() uint64 {
return uint64(f.entry.Count())
}
func (f *field) Offset() uint64 {
if f.Type().Size()*f.Count() <= 4 {
return 0
}
offsetBytes := f.entry.ValueOffset()
return uint64(f.value.Order().Uint32(offsetBytes[:]))
}
func (f *field) Value() FieldValue {
return f.value
}
func (f *field) ParsedValue() interface{} {
tp := f.Type()
buf := f.value.Bytes()
bo := f.value.Order()
count := f.Count()
if count == 0 {
switch tp.ID() {
case FTAscii.ID():
return ""
case FTByte.ID():
return byte(0)
case FTSByte.ID():
return int8(0)
case FTShort.ID():
return uint16(0)
case FTSShort.ID():
return int16(0)
case FTIFD.ID():
return uint32(0)
case FTLong.ID():
return uint32(0)
case FTFloat.ID():
return float32(0.0)
case FTDouble.ID():
return float64(0.0)
default:
return nil
}
}
if tp.ID() == FTAscii.ID() {
if repr := tp.Repr(); repr != nil {
return repr(buf, f.Value().Order())
}
return fmt.Sprintf("%v", buf)
}
size := tp.Size()
var vals []interface{}
for len(buf) > 0 {
in := buf[:size]
buf = buf[size:]
var v interface{}
switch tp.ID() {
case FTByte.ID():
v = in[0]
case FTSByte.ID():
v = int8(in[0])
case FTShort.ID():
v = bo.Uint16(in)
case FTSShort.ID():
v = int16(bo.Uint16(in))
case FTIFD.ID():
fallthrough
case FTLong.ID():
v = bo.Uint32(in)
case FTRational.ID():
denom := int64(bo.Uint32(in[4:]))
if denom != 0 {
v = big.NewRat(int64(bo.Uint32(in)), denom)
} else {
// Prevent panics due to poorly written Rational fields with a denominator of 0.
v = big.Rat{}
}
case FTSRational.ID():
denom := int64(int32(bo.Uint32(in[4:])))
if denom != 0 {
v = big.NewRat(int64(int32(bo.Uint32(in))), denom)
} else {
// Prevent panics due to poorly written Rational fields with a denominator of 0.
v = big.Rat{}
}
case FTFloat.ID():
v = math.Float32frombits(bo.Uint32(in))
case FTDouble.ID():
v = math.Float64frombits(bo.Uint64(in))
default:
v = []byte(in)
}
vals = append(vals, v)
}
if count == 1 {
return vals[0]
}
return vals
}
func (f *field) String() string {
var (
theTSP = f.tsp
theFTSP = f.ftsp
)
if theTSP == nil {
theTSP = DefaultTagSpace
}
if theFTSP == nil {
theFTSP = DefaultFieldTypeSpace
}
var valueRep string
switch f.Type().ReflectType().Kind() {
case reflect.String:
if GetTiffFieldPrintFullFieldValue() {
valueRep = fmt.Sprintf("%q", f.value.Bytes()[:f.Count()])
} else {
if f.Count() > 40 {
valueRep = fmt.Sprintf("%q...", f.value.Bytes()[:41])
} else {
valueRep = fmt.Sprintf("%q", f.value.Bytes()[:f.Count()])
}
}
default:
// For general display purposes, don't show more than maxItems
// amount of elements. In this case, 10 is reasonable. Beyond
// 10 starts to line wrap in some cases like rationals. If
// we encounter that the value contains more than 10, we append
// the ... to the end during string formatting to indicate that
// there were more values, but they are not displayed here.
const maxItems = 10
buf := f.Value().Bytes()
size := f.Type().Size()
count := f.Count()
if !GetTiffFieldPrintFullFieldValue() {
if count > maxItems {
count = maxItems
buf = buf[:count*size]
}
}
vals := make([]string, 0, count)
for len(buf) > 0 {
if f.Type().Repr() != nil {
vals = append(vals, f.Type().Repr()(buf[:size], f.Value().Order()))
} else {
vals = append(vals, fmt.Sprintf("%v", buf[:size]))
}
buf = buf[size:]
}
if count == 1 {
valueRep = vals[0]
} else {
if GetTiffFieldPrintFullFieldValue() {
valueRep = fmt.Sprintf("%v", vals[:f.Count()])
} else {
// Keep a limit of 40 base characters when printing
var totalLen, stop int
for i := range vals {
totalLen += len(vals[i])
if totalLen > 40 {
stop = i
break
}
totalLen += 1 // account for space between values
}
if stop > 0 {
vals = vals[:stop]
}
if stop > 0 || f.Count() > maxItems {
valueRep = fmt.Sprintf("%v...", vals)
} else {
valueRep = fmt.Sprintf("%v", vals[:f.Count()])
}
}
}
}
tagID := f.Tag().ID()
return fmt.Sprintf(`<Tag: (%#04x/%05[1]d) %v Type: (%02d) %v Count: %d Offset: %d Value: %s FieldTypeSpace: %q TagSpaceSet: "%s.%s">`,
tagID, f.Tag().Name(), f.Type().ID(), f.Type().Name(), f.Count(), f.Offset(), valueRep,
theFTSP.Name(), theTSP.Name(), theTSP.GetTagSetNameFromTag(tagID))
}
func (f *field) MarshalJSON() ([]byte, error) {
tmp := struct {
E Entry `json:"Entry"`
V FieldValue `json:"FieldValue"`
FTSP FieldTypeSpace `json:"FieldTypeSpace"`
TSP TagSpace `json:"TagSpace"`
}{
E: f.entry,
V: f.value,
FTSP: f.ftsp,
TSP: f.tsp,
}
return json.Marshal(tmp)
}
func ParseField(br BReader, tsp TagSpace, ftsp FieldTypeSpace) (out Field, err error) {
if ftsp == nil {
ftsp = DefaultFieldTypeSpace
}
if tsp == nil {
tsp = DefaultTagSpace
}
f := &field{ftsp: ftsp, tsp: tsp}
if f.entry, err = ParseEntry(br); err != nil {
return
}
fv := &fieldValue{order: br.ByteOrder()}
valSize := int64(f.Count()) * int64(f.Type().Size())
valOffBytes := f.entry.ValueOffset()
if valSize > 4 {
fv.value = make([]byte, valSize)
offset := int64(br.ByteOrder().Uint32(valOffBytes[:]))
if err = br.BReadSection(&fv.value, offset, valSize); err != nil {
return
}
} else {
fv.value = valOffBytes[:]
}
f.value = fv
return f, nil
}