iptc.go 2.8 KB

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