iptc.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package iptc
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "math"
  9. )
  10. var (
  11. iptcTagHeader = byte(0x1c)
  12. errInvalidDataSize = errors.New("invalid IPTC data size")
  13. )
  14. type IptcMap map[TagKey][]TagValue
  15. func (m IptcMap) AddTag(key TagKey, data []byte) error {
  16. info, infoFound := tagInfoMap[key]
  17. if !infoFound {
  18. return fmt.Errorf("unknown tag %d:%d", key.RecordID, key.TagID)
  19. }
  20. dataSize := len(data)
  21. if dataSize < info.MinSize || dataSize > info.MaxSize {
  22. return fmt.Errorf("invalid tag data size. Min: %d, Max: %d, Has: %d", info.MinSize, info.MaxSize, dataSize)
  23. }
  24. value := TagValue{info.Format, data}
  25. if info.Repeatable {
  26. m[key] = append(m[key], value)
  27. } else {
  28. m[key] = []TagValue{value}
  29. }
  30. return nil
  31. }
  32. func (m IptcMap) MarshalJSON() ([]byte, error) {
  33. mm := make(map[string]interface{}, len(m))
  34. for key, values := range m {
  35. info, infoFound := tagInfoMap[key]
  36. if !infoFound {
  37. continue
  38. }
  39. if info.Repeatable {
  40. mm[info.Title] = values
  41. } else {
  42. mm[info.Title] = values[0]
  43. }
  44. // Add some additional fields for backward compatibility
  45. if key.RecordID == 2 {
  46. if key.TagID == 5 {
  47. mm["Name"] = values[0]
  48. } else if key.TagID == 120 {
  49. mm["Caption"] = values[0]
  50. }
  51. }
  52. }
  53. return json.Marshal(mm)
  54. }
  55. func Parse(data []byte, m IptcMap) error {
  56. buf := bytes.NewBuffer(data)
  57. // Min tag size is 5 (2 tagHeader)
  58. for buf.Len() >= 5 {
  59. if buf.Next(1)[0] != iptcTagHeader {
  60. continue
  61. }
  62. recordID, _ := buf.ReadByte()
  63. tagID, _ := buf.ReadByte()
  64. dataSize16 := binary.BigEndian.Uint16(buf.Next(2))
  65. var dataSize int
  66. if dataSize16 < 32768 {
  67. dataSize = int(dataSize16)
  68. } else {
  69. dataSizeSize := dataSize16 & 32767
  70. switch dataSizeSize {
  71. case 4:
  72. dataSize32 := uint32(0)
  73. if err := binary.Read(buf, binary.BigEndian, &dataSize32); err != nil {
  74. return fmt.Errorf("%s: %s", errInvalidDataSize, err)
  75. }
  76. dataSize = int(dataSize32)
  77. case 8:
  78. dataSize64 := uint64(0)
  79. if err := binary.Read(buf, binary.BigEndian, &dataSize64); err != nil {
  80. return fmt.Errorf("%s: %s", errInvalidDataSize, err)
  81. }
  82. dataSize = int(dataSize64)
  83. default:
  84. return errInvalidDataSize
  85. }
  86. }
  87. // Ignore errors here. If tag is invalid, just don't add it
  88. m.AddTag(TagKey{recordID, tagID}, buf.Next(dataSize))
  89. }
  90. return nil
  91. }
  92. func (m IptcMap) Dump() []byte {
  93. buf := new(bytes.Buffer)
  94. for key, values := range m {
  95. for _, value := range values {
  96. dataSize := len(value.Raw)
  97. // Skip tags with too big data size
  98. if dataSize > math.MaxUint32 {
  99. continue
  100. }
  101. buf.WriteByte(iptcTagHeader)
  102. buf.WriteByte(key.RecordID)
  103. buf.WriteByte(key.TagID)
  104. if dataSize < (1 << 15) {
  105. binary.Write(buf, binary.BigEndian, uint16(dataSize))
  106. } else {
  107. binary.Write(buf, binary.BigEndian, uint16(4+(1<<15)))
  108. binary.Write(buf, binary.BigEndian, uint32(dataSize))
  109. }
  110. buf.Write(value.Raw)
  111. }
  112. }
  113. return buf.Bytes()
  114. }