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 }