ifd_builder.go (29578B)
1 package exif 2 3 // NOTES: 4 // 5 // The thumbnail offset and length tags shouldn't be set directly. Use the 6 // (*IfdBuilder).SetThumbnail() method instead. 7 8 import ( 9 "errors" 10 "fmt" 11 "strings" 12 13 "encoding/binary" 14 15 "github.com/dsoprea/go-logging" 16 17 "github.com/dsoprea/go-exif/v3/common" 18 "github.com/dsoprea/go-exif/v3/undefined" 19 ) 20 21 var ( 22 ifdBuilderLogger = log.NewLogger("exif.ifd_builder") 23 ) 24 25 var ( 26 ErrTagEntryNotFound = errors.New("tag entry not found") 27 ErrChildIbNotFound = errors.New("child IB not found") 28 ) 29 30 type IfdBuilderTagValue struct { 31 valueBytes []byte 32 ib *IfdBuilder 33 } 34 35 func (ibtv IfdBuilderTagValue) String() string { 36 if ibtv.IsBytes() == true { 37 var valuePhrase string 38 if len(ibtv.valueBytes) <= 8 { 39 valuePhrase = fmt.Sprintf("%v", ibtv.valueBytes) 40 } else { 41 valuePhrase = fmt.Sprintf("%v...", ibtv.valueBytes[:8]) 42 } 43 44 return fmt.Sprintf("IfdBuilderTagValue<BYTES=%v LEN=(%d)>", valuePhrase, len(ibtv.valueBytes)) 45 } else if ibtv.IsIb() == true { 46 return fmt.Sprintf("IfdBuilderTagValue<IB=%s>", ibtv.ib) 47 } else { 48 log.Panicf("IBTV state undefined") 49 return "" 50 } 51 } 52 53 func NewIfdBuilderTagValueFromBytes(valueBytes []byte) *IfdBuilderTagValue { 54 return &IfdBuilderTagValue{ 55 valueBytes: valueBytes, 56 } 57 } 58 59 func NewIfdBuilderTagValueFromIfdBuilder(ib *IfdBuilder) *IfdBuilderTagValue { 60 return &IfdBuilderTagValue{ 61 ib: ib, 62 } 63 } 64 65 // IsBytes returns true if the bytes are populated. This is always the case 66 // when we're loaded from a tag in an existing IFD. 67 func (ibtv IfdBuilderTagValue) IsBytes() bool { 68 return ibtv.valueBytes != nil 69 } 70 71 func (ibtv IfdBuilderTagValue) Bytes() []byte { 72 if ibtv.IsBytes() == false { 73 log.Panicf("this tag is not a byte-slice value") 74 } else if ibtv.IsIb() == true { 75 log.Panicf("this tag is an IFD-builder value not a byte-slice") 76 } 77 78 return ibtv.valueBytes 79 } 80 81 func (ibtv IfdBuilderTagValue) IsIb() bool { 82 return ibtv.ib != nil 83 } 84 85 func (ibtv IfdBuilderTagValue) Ib() *IfdBuilder { 86 if ibtv.IsIb() == false { 87 log.Panicf("this tag is not an IFD-builder value") 88 } else if ibtv.IsBytes() == true { 89 log.Panicf("this tag is a byte-slice, not a IFD-builder") 90 } 91 92 return ibtv.ib 93 } 94 95 type BuilderTag struct { 96 // ifdPath is the path of the IFD that hosts this tag. 97 ifdPath string 98 99 tagId uint16 100 typeId exifcommon.TagTypePrimitive 101 102 // value is either a value that can be encoded, an IfdBuilder instance (for 103 // child IFDs), or an IfdTagEntry instance representing an existing, 104 // previously-stored tag. 105 value *IfdBuilderTagValue 106 107 // byteOrder is the byte order. It's chiefly/originally here to support 108 // printing the value. 109 byteOrder binary.ByteOrder 110 } 111 112 func NewBuilderTag(ifdPath string, tagId uint16, typeId exifcommon.TagTypePrimitive, value *IfdBuilderTagValue, byteOrder binary.ByteOrder) *BuilderTag { 113 return &BuilderTag{ 114 ifdPath: ifdPath, 115 tagId: tagId, 116 typeId: typeId, 117 value: value, 118 byteOrder: byteOrder, 119 } 120 } 121 122 func NewChildIfdBuilderTag(ifdPath string, tagId uint16, value *IfdBuilderTagValue) *BuilderTag { 123 return &BuilderTag{ 124 ifdPath: ifdPath, 125 tagId: tagId, 126 typeId: exifcommon.TypeLong, 127 value: value, 128 } 129 } 130 131 func (bt *BuilderTag) Value() (value *IfdBuilderTagValue) { 132 return bt.value 133 } 134 135 func (bt *BuilderTag) String() string { 136 var valueString string 137 138 if bt.value.IsBytes() == true { 139 var err error 140 141 valueString, err = exifcommon.FormatFromBytes(bt.value.Bytes(), bt.typeId, false, bt.byteOrder) 142 log.PanicIf(err) 143 } else { 144 valueString = fmt.Sprintf("%v", bt.value) 145 } 146 147 return fmt.Sprintf("BuilderTag<IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] VALUE=[%s]>", bt.ifdPath, bt.tagId, bt.typeId.String(), valueString) 148 } 149 150 func (bt *BuilderTag) SetValue(byteOrder binary.ByteOrder, value interface{}) (err error) { 151 defer func() { 152 if state := recover(); state != nil { 153 err = log.Wrap(state.(error)) 154 } 155 }() 156 157 // TODO(dustin): !! Add test. 158 159 var ed exifcommon.EncodedData 160 if bt.typeId == exifcommon.TypeUndefined { 161 encodeable := value.(exifundefined.EncodeableValue) 162 163 encoded, unitCount, err := exifundefined.Encode(encodeable, byteOrder) 164 log.PanicIf(err) 165 166 ed = exifcommon.EncodedData{ 167 Type: exifcommon.TypeUndefined, 168 Encoded: encoded, 169 UnitCount: unitCount, 170 } 171 } else { 172 ve := exifcommon.NewValueEncoder(byteOrder) 173 174 var err error 175 176 ed, err = ve.Encode(value) 177 log.PanicIf(err) 178 } 179 180 bt.value = NewIfdBuilderTagValueFromBytes(ed.Encoded) 181 182 return nil 183 } 184 185 // NewStandardBuilderTag constructs a `BuilderTag` instance. The type is looked 186 // up. `ii` is the type of IFD that owns this tag. 187 func NewStandardBuilderTag(ifdPath string, it *IndexedTag, byteOrder binary.ByteOrder, value interface{}) *BuilderTag { 188 // If there is more than one supported type, we'll go with the larger to 189 // encode with. It'll use the same amount of fixed-space, and we'll 190 // eliminate unnecessary overflows/issues. 191 tagType := it.GetEncodingType(value) 192 193 var rawBytes []byte 194 if it.DoesSupportType(exifcommon.TypeUndefined) == true { 195 encodeable := value.(exifundefined.EncodeableValue) 196 197 var err error 198 199 rawBytes, _, err = exifundefined.Encode(encodeable, byteOrder) 200 log.PanicIf(err) 201 } else { 202 ve := exifcommon.NewValueEncoder(byteOrder) 203 204 ed, err := ve.Encode(value) 205 log.PanicIf(err) 206 207 rawBytes = ed.Encoded 208 } 209 210 tagValue := NewIfdBuilderTagValueFromBytes(rawBytes) 211 212 return NewBuilderTag( 213 ifdPath, 214 it.Id, 215 tagType, 216 tagValue, 217 byteOrder) 218 } 219 220 type IfdBuilder struct { 221 ifdIdentity *exifcommon.IfdIdentity 222 223 byteOrder binary.ByteOrder 224 225 // Includes both normal tags and IFD tags (which point to child IFDs). 226 // TODO(dustin): Keep a separate list of children like with `Ifd`. 227 // TODO(dustin): Either rename this or `Entries` in `Ifd` to be the same thing. 228 tags []*BuilderTag 229 230 // existingOffset will be the offset that this IFD is currently found at if 231 // it represents an IFD that has previously been stored (or 0 if not). 232 existingOffset uint32 233 234 // nextIb represents the next link if we're chaining to another. 235 nextIb *IfdBuilder 236 237 // thumbnailData is populated with thumbnail data if there was thumbnail 238 // data. Otherwise, it's nil. 239 thumbnailData []byte 240 241 ifdMapping *exifcommon.IfdMapping 242 tagIndex *TagIndex 243 } 244 245 func NewIfdBuilder(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exifcommon.IfdIdentity, byteOrder binary.ByteOrder) (ib *IfdBuilder) { 246 ib = &IfdBuilder{ 247 ifdIdentity: ii, 248 249 byteOrder: byteOrder, 250 tags: make([]*BuilderTag, 0), 251 252 ifdMapping: ifdMapping, 253 tagIndex: tagIndex, 254 } 255 256 return ib 257 } 258 259 // NewIfdBuilderWithExistingIfd creates a new IB using the same header type 260 // information as the given IFD. 261 func NewIfdBuilderWithExistingIfd(ifd *Ifd) (ib *IfdBuilder) { 262 ib = &IfdBuilder{ 263 ifdIdentity: ifd.IfdIdentity(), 264 265 byteOrder: ifd.ByteOrder(), 266 existingOffset: ifd.Offset(), 267 ifdMapping: ifd.ifdMapping, 268 tagIndex: ifd.tagIndex, 269 } 270 271 return ib 272 } 273 274 // NewIfdBuilderFromExistingChain creates a chain of IB instances from an 275 // IFD chain generated from real data. 276 func NewIfdBuilderFromExistingChain(rootIfd *Ifd) (firstIb *IfdBuilder) { 277 var lastIb *IfdBuilder 278 i := 0 279 for thisExistingIfd := rootIfd; thisExistingIfd != nil; thisExistingIfd = thisExistingIfd.nextIfd { 280 newIb := NewIfdBuilder( 281 rootIfd.ifdMapping, 282 rootIfd.tagIndex, 283 rootIfd.ifdIdentity, 284 thisExistingIfd.ByteOrder()) 285 286 if firstIb == nil { 287 firstIb = newIb 288 } else { 289 lastIb.SetNextIb(newIb) 290 } 291 292 err := newIb.AddTagsFromExisting(thisExistingIfd, nil, nil) 293 log.PanicIf(err) 294 295 lastIb = newIb 296 i++ 297 } 298 299 return firstIb 300 } 301 302 func (ib *IfdBuilder) IfdIdentity() *exifcommon.IfdIdentity { 303 return ib.ifdIdentity 304 } 305 306 func (ib *IfdBuilder) NextIb() (nextIb *IfdBuilder, err error) { 307 return ib.nextIb, nil 308 } 309 310 func (ib *IfdBuilder) ChildWithTagId(childIfdTagId uint16) (childIb *IfdBuilder, err error) { 311 defer func() { 312 if state := recover(); state != nil { 313 err = log.Wrap(state.(error)) 314 } 315 }() 316 317 for _, bt := range ib.tags { 318 if bt.value.IsIb() == false { 319 continue 320 } 321 322 childIbThis := bt.value.Ib() 323 324 if childIbThis.IfdIdentity().TagId() == childIfdTagId { 325 return childIbThis, nil 326 } 327 } 328 329 log.Panic(ErrChildIbNotFound) 330 331 // Never reached. 332 return nil, nil 333 } 334 335 func getOrCreateIbFromRootIbInner(rootIb *IfdBuilder, parentIb *IfdBuilder, currentLineage []exifcommon.IfdTagIdAndIndex) (ib *IfdBuilder, err error) { 336 defer func() { 337 if state := recover(); state != nil { 338 err = log.Wrap(state.(error)) 339 } 340 }() 341 342 // TODO(dustin): !! Add test. 343 344 thisIb := rootIb 345 346 // Since we're calling ourselves recursively with incrementally different 347 // paths, the FQ IFD-path of the parent that called us needs to be passed 348 // in, in order for us to know it. 349 var parentLineage []exifcommon.IfdTagIdAndIndex 350 if parentIb != nil { 351 var err error 352 353 parentLineage, err = thisIb.ifdMapping.ResolvePath(parentIb.IfdIdentity().String()) 354 log.PanicIf(err) 355 } 356 357 // Process the current path part. 358 currentItIi := currentLineage[0] 359 360 // Make sure the leftmost part of the FQ IFD-path agrees with the IB we 361 // were given. 362 363 expectedFqRootIfdPath := "" 364 if parentLineage != nil { 365 expectedLineage := append(parentLineage, currentItIi) 366 expectedFqRootIfdPath = thisIb.ifdMapping.PathPhraseFromLineage(expectedLineage) 367 } else { 368 expectedFqRootIfdPath = thisIb.ifdMapping.PathPhraseFromLineage(currentLineage[:1]) 369 } 370 371 if expectedFqRootIfdPath != thisIb.IfdIdentity().String() { 372 log.Panicf("the FQ IFD-path [%s] we were given does not match the builder's FQ IFD-path [%s]", expectedFqRootIfdPath, thisIb.IfdIdentity().String()) 373 } 374 375 // If we actually wanted a sibling (currentItIi.Index > 0) then seek to it, 376 // appending new siblings, as required, until we get there. 377 for i := 0; i < currentItIi.Index; i++ { 378 if thisIb.nextIb == nil { 379 // Generate an FQ IFD-path for the sibling. It'll use the same 380 // non-FQ IFD-path as the current IB. 381 382 iiSibling := thisIb.IfdIdentity().NewSibling(i + 1) 383 thisIb.nextIb = NewIfdBuilder(thisIb.ifdMapping, thisIb.tagIndex, iiSibling, thisIb.byteOrder) 384 } 385 386 thisIb = thisIb.nextIb 387 } 388 389 // There is no child IFD to process. We're done. 390 if len(currentLineage) == 1 { 391 return thisIb, nil 392 } 393 394 // Establish the next child to be processed. 395 396 childItii := currentLineage[1] 397 398 var foundChild *IfdBuilder 399 for _, bt := range thisIb.tags { 400 if bt.value.IsIb() == false { 401 continue 402 } 403 404 childIb := bt.value.Ib() 405 406 if childIb.IfdIdentity().TagId() == childItii.TagId { 407 foundChild = childIb 408 break 409 } 410 } 411 412 // If we didn't find the child, add it. 413 414 if foundChild == nil { 415 currentIfdTag := thisIb.IfdIdentity().IfdTag() 416 417 childIfdTag := 418 exifcommon.NewIfdTag( 419 ¤tIfdTag, 420 childItii.TagId, 421 childItii.Name) 422 423 iiChild := thisIb.IfdIdentity().NewChild(childIfdTag, 0) 424 425 foundChild = 426 NewIfdBuilder( 427 thisIb.ifdMapping, 428 thisIb.tagIndex, 429 iiChild, 430 thisIb.byteOrder) 431 432 err = thisIb.AddChildIb(foundChild) 433 log.PanicIf(err) 434 } 435 436 finalIb, err := getOrCreateIbFromRootIbInner(foundChild, thisIb, currentLineage[1:]) 437 log.PanicIf(err) 438 439 return finalIb, nil 440 } 441 442 // GetOrCreateIbFromRootIb returns an IB representing the requested IFD, even if 443 // an IB doesn't already exist for it. This function may call itself 444 // recursively. 445 func GetOrCreateIbFromRootIb(rootIb *IfdBuilder, fqIfdPath string) (ib *IfdBuilder, err error) { 446 defer func() { 447 if state := recover(); state != nil { 448 err = log.Wrap(state.(error)) 449 } 450 }() 451 452 // lineage is a necessity of our recursion process. It doesn't include any 453 // parent IFDs on its left-side; it starts with the current IB only. 454 lineage, err := rootIb.ifdMapping.ResolvePath(fqIfdPath) 455 log.PanicIf(err) 456 457 ib, err = getOrCreateIbFromRootIbInner(rootIb, nil, lineage) 458 log.PanicIf(err) 459 460 return ib, nil 461 } 462 463 func (ib *IfdBuilder) String() string { 464 nextIfdPhrase := "" 465 if ib.nextIb != nil { 466 // TODO(dustin): We were setting this to ii.String(), but we were getting hex-data when printing this after building from an existing chain. 467 nextIfdPhrase = ib.nextIb.IfdIdentity().UnindexedString() 468 } 469 470 return fmt.Sprintf("IfdBuilder<PATH=[%s] TAG-ID=(0x%04x) COUNT=(%d) OFF=(0x%04x) NEXT-IFD-PATH=[%s]>", ib.IfdIdentity().UnindexedString(), ib.IfdIdentity().TagId(), len(ib.tags), ib.existingOffset, nextIfdPhrase) 471 } 472 473 func (ib *IfdBuilder) Tags() (tags []*BuilderTag) { 474 return ib.tags 475 } 476 477 // SetThumbnail sets thumbnail data. 478 // 479 // NOTES: 480 // 481 // - We don't manage any facet of the thumbnail data. This is the 482 // responsibility of the user/developer. 483 // - This method will fail unless the thumbnail is set on a the root IFD. 484 // However, in order to be valid, it must be set on the second one, linked to 485 // by the first, as per the EXIF/TIFF specification. 486 // - We set the offset to (0) now but will allocate the data and properly assign 487 // the offset when the IB is encoded (later). 488 func (ib *IfdBuilder) SetThumbnail(data []byte) (err error) { 489 defer func() { 490 if state := recover(); state != nil { 491 err = log.Wrap(state.(error)) 492 } 493 }() 494 495 if ib.IfdIdentity().UnindexedString() != exifcommon.IfdStandardIfdIdentity.UnindexedString() { 496 log.Panicf("thumbnails can only go into a root Ifd (and only the second one)") 497 } 498 499 // TODO(dustin): !! Add a test for this function. 500 501 if data == nil || len(data) == 0 { 502 log.Panic("thumbnail is empty") 503 } 504 505 ib.thumbnailData = data 506 507 ibtvfb := NewIfdBuilderTagValueFromBytes(ib.thumbnailData) 508 offsetBt := 509 NewBuilderTag( 510 ib.IfdIdentity().UnindexedString(), 511 ThumbnailOffsetTagId, 512 exifcommon.TypeLong, 513 ibtvfb, 514 ib.byteOrder) 515 516 err = ib.Set(offsetBt) 517 log.PanicIf(err) 518 519 thumbnailSizeIt, err := ib.tagIndex.Get(ib.IfdIdentity(), ThumbnailSizeTagId) 520 log.PanicIf(err) 521 522 sizeBt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), thumbnailSizeIt, ib.byteOrder, []uint32{uint32(len(ib.thumbnailData))}) 523 524 err = ib.Set(sizeBt) 525 log.PanicIf(err) 526 527 return nil 528 } 529 530 func (ib *IfdBuilder) Thumbnail() []byte { 531 return ib.thumbnailData 532 } 533 534 func (ib *IfdBuilder) printTagTree(levels int) { 535 indent := strings.Repeat(" ", levels*2) 536 537 i := 0 538 for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb { 539 prefix := " " 540 if i > 0 { 541 prefix = ">" 542 } 543 544 if levels == 0 { 545 fmt.Printf("%s%sIFD: %s INDEX=(%d)\n", indent, prefix, currentIb, i) 546 } else { 547 fmt.Printf("%s%sChild IFD: %s\n", indent, prefix, currentIb) 548 } 549 550 if len(currentIb.tags) > 0 { 551 fmt.Printf("\n") 552 553 for i, tag := range currentIb.tags { 554 isChildIb := false 555 _, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId) 556 if err == nil { 557 isChildIb = true 558 } else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false { 559 log.Panic(err) 560 } 561 562 tagName := "" 563 564 // If a normal tag (not a child IFD) get the name. 565 if isChildIb == true { 566 tagName = "<Child IFD>" 567 } else { 568 it, err := ib.tagIndex.Get(ib.ifdIdentity, tag.tagId) 569 if log.Is(err, ErrTagNotFound) == true { 570 tagName = "<UNKNOWN>" 571 } else if err != nil { 572 log.Panic(err) 573 } else { 574 tagName = it.Name 575 } 576 } 577 578 value := tag.Value() 579 580 if value.IsIb() == true { 581 fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, value.Ib()) 582 } else { 583 fmt.Printf("%s (%d): [%s] %s\n", indent, i, tagName, tag) 584 } 585 586 if isChildIb == true { 587 if tag.value.IsIb() == false { 588 log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag) 589 } 590 591 fmt.Printf("\n") 592 593 childIb := tag.value.Ib() 594 childIb.printTagTree(levels + 1) 595 } 596 } 597 598 fmt.Printf("\n") 599 } 600 601 i++ 602 } 603 } 604 605 func (ib *IfdBuilder) PrintTagTree() { 606 ib.printTagTree(0) 607 } 608 609 func (ib *IfdBuilder) printIfdTree(levels int) { 610 indent := strings.Repeat(" ", levels*2) 611 612 i := 0 613 for currentIb := ib; currentIb != nil; currentIb = currentIb.nextIb { 614 prefix := " " 615 if i > 0 { 616 prefix = ">" 617 } 618 619 fmt.Printf("%s%s%s\n", indent, prefix, currentIb) 620 621 if len(currentIb.tags) > 0 { 622 for _, tag := range currentIb.tags { 623 isChildIb := false 624 _, err := ib.ifdMapping.GetChild(currentIb.IfdIdentity().UnindexedString(), tag.tagId) 625 if err == nil { 626 isChildIb = true 627 } else if log.Is(err, exifcommon.ErrChildIfdNotMapped) == false { 628 log.Panic(err) 629 } 630 631 if isChildIb == true { 632 if tag.value.IsIb() == false { 633 log.Panicf("tag-ID (0x%04x) is an IFD but the tag value is not an IB instance: %v", tag.tagId, tag) 634 } 635 636 childIb := tag.value.Ib() 637 childIb.printIfdTree(levels + 1) 638 } 639 } 640 } 641 642 i++ 643 } 644 } 645 646 func (ib *IfdBuilder) PrintIfdTree() { 647 ib.printIfdTree(0) 648 } 649 650 func (ib *IfdBuilder) dumpToStrings(thisIb *IfdBuilder, prefix string, tagId uint16, lines []string) (linesOutput []string) { 651 if lines == nil { 652 linesOutput = make([]string, 0) 653 } else { 654 linesOutput = lines 655 } 656 657 siblingIfdIndex := 0 658 for ; thisIb != nil; thisIb = thisIb.nextIb { 659 line := fmt.Sprintf("IFD<PARENTS=[%s] FQ-IFD-PATH=[%s] IFD-INDEX=(%d) IFD-TAG-ID=(0x%04x) TAG=[0x%04x]>", prefix, thisIb.IfdIdentity().String(), siblingIfdIndex, thisIb.IfdIdentity().TagId(), tagId) 660 linesOutput = append(linesOutput, line) 661 662 for i, tag := range thisIb.tags { 663 var childIb *IfdBuilder 664 childIfdName := "" 665 if tag.value.IsIb() == true { 666 childIb = tag.value.Ib() 667 childIfdName = childIb.IfdIdentity().UnindexedString() 668 } 669 670 line := fmt.Sprintf("TAG<PARENTS=[%s] FQ-IFD-PATH=[%s] IFD-TAG-ID=(0x%04x) CHILD-IFD=[%s] TAG-INDEX=(%d) TAG=[0x%04x]>", prefix, thisIb.IfdIdentity().String(), thisIb.IfdIdentity().TagId(), childIfdName, i, tag.tagId) 671 linesOutput = append(linesOutput, line) 672 673 if childIb == nil { 674 continue 675 } 676 677 childPrefix := "" 678 if prefix == "" { 679 childPrefix = fmt.Sprintf("%s", thisIb.IfdIdentity().UnindexedString()) 680 } else { 681 childPrefix = fmt.Sprintf("%s->%s", prefix, thisIb.IfdIdentity().UnindexedString()) 682 } 683 684 linesOutput = thisIb.dumpToStrings(childIb, childPrefix, tag.tagId, linesOutput) 685 } 686 687 siblingIfdIndex++ 688 } 689 690 return linesOutput 691 } 692 693 func (ib *IfdBuilder) DumpToStrings() (lines []string) { 694 return ib.dumpToStrings(ib, "", 0, lines) 695 } 696 697 func (ib *IfdBuilder) SetNextIb(nextIb *IfdBuilder) (err error) { 698 defer func() { 699 if state := recover(); state != nil { 700 err = log.Wrap(state.(error)) 701 } 702 }() 703 704 ib.nextIb = nextIb 705 706 return nil 707 } 708 709 func (ib *IfdBuilder) DeleteN(tagId uint16, n int) (err error) { 710 defer func() { 711 if state := recover(); state != nil { 712 err = log.Wrap(state.(error)) 713 } 714 }() 715 716 if n < 1 { 717 log.Panicf("N must be at least 1: (%d)", n) 718 } 719 720 for n > 0 { 721 j := -1 722 for i, bt := range ib.tags { 723 if bt.tagId == tagId { 724 j = i 725 break 726 } 727 } 728 729 if j == -1 { 730 log.Panic(ErrTagEntryNotFound) 731 } 732 733 ib.tags = append(ib.tags[:j], ib.tags[j+1:]...) 734 n-- 735 } 736 737 return nil 738 } 739 740 func (ib *IfdBuilder) DeleteFirst(tagId uint16) (err error) { 741 defer func() { 742 if state := recover(); state != nil { 743 err = log.Wrap(state.(error)) 744 } 745 }() 746 747 err = ib.DeleteN(tagId, 1) 748 log.PanicIf(err) 749 750 return nil 751 } 752 753 func (ib *IfdBuilder) DeleteAll(tagId uint16) (n int, err error) { 754 defer func() { 755 if state := recover(); state != nil { 756 err = log.Wrap(state.(error)) 757 } 758 }() 759 760 for { 761 err = ib.DeleteN(tagId, 1) 762 if log.Is(err, ErrTagEntryNotFound) == true { 763 break 764 } else if err != nil { 765 log.Panic(err) 766 } 767 768 n++ 769 } 770 771 return n, nil 772 } 773 774 func (ib *IfdBuilder) ReplaceAt(position int, bt *BuilderTag) (err error) { 775 defer func() { 776 if state := recover(); state != nil { 777 err = log.Wrap(state.(error)) 778 } 779 }() 780 781 if position < 0 { 782 log.Panicf("replacement position must be 0 or greater") 783 } else if position >= len(ib.tags) { 784 log.Panicf("replacement position does not exist") 785 } 786 787 ib.tags[position] = bt 788 789 return nil 790 } 791 792 func (ib *IfdBuilder) Replace(tagId uint16, bt *BuilderTag) (err error) { 793 defer func() { 794 if state := recover(); state != nil { 795 err = log.Wrap(state.(error)) 796 } 797 }() 798 799 position, err := ib.Find(tagId) 800 log.PanicIf(err) 801 802 ib.tags[position] = bt 803 804 return nil 805 } 806 807 // Set will add a new entry or update an existing entry. 808 func (ib *IfdBuilder) Set(bt *BuilderTag) (err error) { 809 defer func() { 810 if state := recover(); state != nil { 811 err = log.Wrap(state.(error)) 812 } 813 }() 814 815 position, err := ib.Find(bt.tagId) 816 if err == nil { 817 ib.tags[position] = bt 818 } else if log.Is(err, ErrTagEntryNotFound) == true { 819 err = ib.add(bt) 820 log.PanicIf(err) 821 } else { 822 log.Panic(err) 823 } 824 825 return nil 826 } 827 828 func (ib *IfdBuilder) FindN(tagId uint16, maxFound int) (found []int, err error) { 829 defer func() { 830 if state := recover(); state != nil { 831 err = log.Wrap(state.(error)) 832 } 833 }() 834 835 found = make([]int, 0) 836 837 for i, bt := range ib.tags { 838 if bt.tagId == tagId { 839 found = append(found, i) 840 if maxFound == 0 || len(found) >= maxFound { 841 break 842 } 843 } 844 } 845 846 return found, nil 847 } 848 849 func (ib *IfdBuilder) Find(tagId uint16) (position int, err error) { 850 defer func() { 851 if state := recover(); state != nil { 852 err = log.Wrap(state.(error)) 853 } 854 }() 855 856 found, err := ib.FindN(tagId, 1) 857 log.PanicIf(err) 858 859 if len(found) == 0 { 860 log.Panic(ErrTagEntryNotFound) 861 } 862 863 return found[0], nil 864 } 865 866 func (ib *IfdBuilder) FindTag(tagId uint16) (bt *BuilderTag, err error) { 867 defer func() { 868 if state := recover(); state != nil { 869 err = log.Wrap(state.(error)) 870 } 871 }() 872 873 found, err := ib.FindN(tagId, 1) 874 log.PanicIf(err) 875 876 if len(found) == 0 { 877 log.Panic(ErrTagEntryNotFound) 878 } 879 880 position := found[0] 881 882 return ib.tags[position], nil 883 } 884 885 func (ib *IfdBuilder) FindTagWithName(tagName string) (bt *BuilderTag, err error) { 886 defer func() { 887 if state := recover(); state != nil { 888 err = log.Wrap(state.(error)) 889 } 890 }() 891 892 it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName) 893 log.PanicIf(err) 894 895 found, err := ib.FindN(it.Id, 1) 896 log.PanicIf(err) 897 898 if len(found) == 0 { 899 log.Panic(ErrTagEntryNotFound) 900 } 901 902 position := found[0] 903 904 return ib.tags[position], nil 905 } 906 907 func (ib *IfdBuilder) add(bt *BuilderTag) (err error) { 908 defer func() { 909 if state := recover(); state != nil { 910 err = log.Wrap(state.(error)) 911 } 912 }() 913 914 if bt.ifdPath == "" { 915 log.Panicf("BuilderTag ifdPath is not set: %s", bt) 916 } else if bt.typeId == 0x0 { 917 log.Panicf("BuilderTag type-ID is not set: %s", bt) 918 } else if bt.value == nil { 919 log.Panicf("BuilderTag value is not set: %s", bt) 920 } 921 922 ib.tags = append(ib.tags, bt) 923 return nil 924 } 925 926 func (ib *IfdBuilder) Add(bt *BuilderTag) (err error) { 927 defer func() { 928 if state := recover(); state != nil { 929 err = log.Wrap(state.(error)) 930 } 931 }() 932 933 if bt.value.IsIb() == true { 934 log.Panicf("child IfdBuilders must be added via AddChildIb() or AddTagsFromExisting(), not Add()") 935 } 936 937 err = ib.add(bt) 938 log.PanicIf(err) 939 940 return nil 941 } 942 943 // AddChildIb adds a tag that branches to a new IFD. 944 func (ib *IfdBuilder) AddChildIb(childIb *IfdBuilder) (err error) { 945 defer func() { 946 if state := recover(); state != nil { 947 err = log.Wrap(state.(error)) 948 } 949 }() 950 951 if childIb.IfdIdentity().TagId() == 0 { 952 log.Panicf("IFD can not be used as a child IFD (not associated with a tag-ID): %v", childIb) 953 } else if childIb.byteOrder != ib.byteOrder { 954 log.Panicf("Child IFD does not have the same byte-order: [%s] != [%s]", childIb.byteOrder, ib.byteOrder) 955 } 956 957 // Since no standard IFDs supports occur`ring more than once, check that a 958 // tag of this type has not been previously added. Note that we just search 959 // the current IFD and *not every* IFD. 960 for _, bt := range childIb.tags { 961 if bt.tagId == childIb.IfdIdentity().TagId() { 962 log.Panicf("child-IFD already added: %v", childIb.IfdIdentity().UnindexedString()) 963 } 964 } 965 966 bt := ib.NewBuilderTagFromBuilder(childIb) 967 ib.tags = append(ib.tags, bt) 968 969 return nil 970 } 971 972 func (ib *IfdBuilder) NewBuilderTagFromBuilder(childIb *IfdBuilder) (bt *BuilderTag) { 973 defer func() { 974 if state := recover(); state != nil { 975 err := log.Wrap(state.(error)) 976 log.Panic(err) 977 } 978 }() 979 980 value := NewIfdBuilderTagValueFromIfdBuilder(childIb) 981 982 bt = NewChildIfdBuilderTag( 983 ib.IfdIdentity().UnindexedString(), 984 childIb.IfdIdentity().TagId(), 985 value) 986 987 return bt 988 } 989 990 // AddTagsFromExisting does a verbatim copy of the entries in `ifd` to this 991 // builder. It excludes child IFDs. These must be added explicitly via 992 // `AddChildIb()`. 993 func (ib *IfdBuilder) AddTagsFromExisting(ifd *Ifd, includeTagIds []uint16, excludeTagIds []uint16) (err error) { 994 defer func() { 995 if state := recover(); state != nil { 996 err = log.Wrap(state.(error)) 997 } 998 }() 999 1000 thumbnailData, err := ifd.Thumbnail() 1001 if err == nil { 1002 err = ib.SetThumbnail(thumbnailData) 1003 log.PanicIf(err) 1004 } else if log.Is(err, ErrNoThumbnail) == false { 1005 log.Panic(err) 1006 } 1007 1008 for i, ite := range ifd.Entries() { 1009 if ite.IsThumbnailOffset() == true || ite.IsThumbnailSize() { 1010 // These will be added on-the-fly when we encode. 1011 continue 1012 } 1013 1014 if excludeTagIds != nil && len(excludeTagIds) > 0 { 1015 found := false 1016 for _, excludedTagId := range excludeTagIds { 1017 if excludedTagId == ite.TagId() { 1018 found = true 1019 } 1020 } 1021 1022 if found == true { 1023 continue 1024 } 1025 } 1026 1027 if includeTagIds != nil && len(includeTagIds) > 0 { 1028 // Whether or not there was a list of excludes, if there is a list 1029 // of includes than the current tag has to be in it. 1030 1031 found := false 1032 for _, includedTagId := range includeTagIds { 1033 if includedTagId == ite.TagId() { 1034 found = true 1035 break 1036 } 1037 } 1038 1039 if found == false { 1040 continue 1041 } 1042 } 1043 1044 var bt *BuilderTag 1045 1046 if ite.ChildIfdPath() != "" { 1047 // If we want to add an IFD tag, we'll have to build it first and 1048 // *then* add it via a different method. 1049 1050 // Figure out which of the child-IFDs that are associated with 1051 // this IFD represents this specific child IFD. 1052 1053 var childIfd *Ifd 1054 for _, thisChildIfd := range ifd.Children() { 1055 if thisChildIfd.ParentTagIndex() != i { 1056 continue 1057 } else if thisChildIfd.ifdIdentity.TagId() != 0xffff && thisChildIfd.ifdIdentity.TagId() != ite.TagId() { 1058 log.Panicf("child-IFD tag is not correct: TAG-POSITION=(%d) ITE=%s CHILD-IFD=%s", thisChildIfd.ParentTagIndex(), ite, thisChildIfd) 1059 } 1060 1061 childIfd = thisChildIfd 1062 break 1063 } 1064 1065 if childIfd == nil { 1066 childTagIds := make([]string, len(ifd.Children())) 1067 for j, childIfd := range ifd.Children() { 1068 childTagIds[j] = fmt.Sprintf("0x%04x (parent tag-position %d)", childIfd.ifdIdentity.TagId(), childIfd.ParentTagIndex()) 1069 } 1070 1071 log.Panicf("could not find child IFD for child ITE: IFD-PATH=[%s] TAG-ID=(0x%04x) CURRENT-TAG-POSITION=(%d) CHILDREN=%v", ite.IfdPath(), ite.TagId(), i, childTagIds) 1072 } 1073 1074 childIb := NewIfdBuilderFromExistingChain(childIfd) 1075 bt = ib.NewBuilderTagFromBuilder(childIb) 1076 } else { 1077 // Non-IFD tag. 1078 1079 rawBytes, err := ite.GetRawBytes() 1080 log.PanicIf(err) 1081 1082 value := NewIfdBuilderTagValueFromBytes(rawBytes) 1083 1084 bt = NewBuilderTag( 1085 ifd.ifdIdentity.UnindexedString(), 1086 ite.TagId(), 1087 ite.TagType(), 1088 value, 1089 ib.byteOrder) 1090 } 1091 1092 err := ib.add(bt) 1093 log.PanicIf(err) 1094 } 1095 1096 return nil 1097 } 1098 1099 // AddStandard quickly and easily composes and adds the tag using the 1100 // information already known about a tag. Only works with standard tags. 1101 func (ib *IfdBuilder) AddStandard(tagId uint16, value interface{}) (err error) { 1102 defer func() { 1103 if state := recover(); state != nil { 1104 err = log.Wrap(state.(error)) 1105 } 1106 }() 1107 1108 it, err := ib.tagIndex.Get(ib.IfdIdentity(), tagId) 1109 log.PanicIf(err) 1110 1111 bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value) 1112 1113 err = ib.add(bt) 1114 log.PanicIf(err) 1115 1116 return nil 1117 } 1118 1119 // AddStandardWithName quickly and easily composes and adds the tag using the 1120 // information already known about a tag (using the name). Only works with 1121 // standard tags. 1122 func (ib *IfdBuilder) AddStandardWithName(tagName string, value interface{}) (err error) { 1123 defer func() { 1124 if state := recover(); state != nil { 1125 err = log.Wrap(state.(error)) 1126 } 1127 }() 1128 1129 it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName) 1130 log.PanicIf(err) 1131 1132 bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value) 1133 1134 err = ib.add(bt) 1135 log.PanicIf(err) 1136 1137 return nil 1138 } 1139 1140 // SetStandard quickly and easily composes and adds or replaces the tag using 1141 // the information already known about a tag. Only works with standard tags. 1142 func (ib *IfdBuilder) SetStandard(tagId uint16, value interface{}) (err error) { 1143 defer func() { 1144 if state := recover(); state != nil { 1145 err = log.Wrap(state.(error)) 1146 } 1147 }() 1148 1149 // TODO(dustin): !! Add test for this function. 1150 1151 it, err := ib.tagIndex.Get(ib.IfdIdentity(), tagId) 1152 log.PanicIf(err) 1153 1154 bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value) 1155 1156 i, err := ib.Find(tagId) 1157 if err != nil { 1158 if log.Is(err, ErrTagEntryNotFound) == false { 1159 log.Panic(err) 1160 } 1161 1162 ib.tags = append(ib.tags, bt) 1163 } else { 1164 ib.tags[i] = bt 1165 } 1166 1167 return nil 1168 } 1169 1170 // SetStandardWithName quickly and easily composes and adds or replaces the 1171 // tag using the information already known about a tag (using the name). Only 1172 // works with standard tags. 1173 func (ib *IfdBuilder) SetStandardWithName(tagName string, value interface{}) (err error) { 1174 defer func() { 1175 if state := recover(); state != nil { 1176 err = log.Wrap(state.(error)) 1177 } 1178 }() 1179 1180 // TODO(dustin): !! Add test for this function. 1181 1182 it, err := ib.tagIndex.GetWithName(ib.IfdIdentity(), tagName) 1183 log.PanicIf(err) 1184 1185 bt := NewStandardBuilderTag(ib.IfdIdentity().UnindexedString(), it, ib.byteOrder, value) 1186 1187 i, err := ib.Find(bt.tagId) 1188 if err != nil { 1189 if log.Is(err, ErrTagEntryNotFound) == false { 1190 log.Panic(err) 1191 } 1192 1193 ib.tags = append(ib.tags, bt) 1194 } else { 1195 ib.tags[i] = bt 1196 } 1197 1198 return nil 1199 }