iptc.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  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. if key.TagID == 5 {
  42. mm["Name"] = values[0]
  43. } else if key.TagID == 120 {
  44. mm["Caption"] = values[0]
  45. }
  46. }
  47. }
  48. return json.Marshal(mm)
  49. }
  50. func Parse(data []byte, m IptcMap) error {
  51. buf := bytes.NewBuffer(data)
  52. // Min tag size is 5 (2 tagHeader)
  53. for buf.Len() >= 5 {
  54. if buf.Next(1)[0] != iptcTagHeader {
  55. continue
  56. }
  57. recordID, _ := buf.ReadByte()
  58. tagID, _ := buf.ReadByte()
  59. dataSize16 := binary.BigEndian.Uint16(buf.Next(2))
  60. var dataSize int
  61. if dataSize16 < 32768 {
  62. dataSize = int(dataSize16)
  63. } else {
  64. dataSizeSize := dataSize16 & 32767
  65. switch dataSizeSize {
  66. case 4:
  67. dataSize32 := uint32(0)
  68. if err := binary.Read(buf, binary.BigEndian, &dataSize32); err != nil {
  69. return newIptcError("invalid IPTC data size: %s", err)
  70. }
  71. dataSize = int(dataSize32)
  72. case 8:
  73. dataSize64 := uint64(0)
  74. if err := binary.Read(buf, binary.BigEndian, &dataSize64); err != nil {
  75. return newIptcError("invalid IPTC data size: %s", err)
  76. }
  77. dataSize = int(dataSize64)
  78. default:
  79. return newIptcError("invalid IPTC data size")
  80. }
  81. }
  82. // Ignore errors here. If tag is invalid, just don't add it
  83. m.AddTag(TagKey{recordID, tagID}, buf.Next(dataSize))
  84. }
  85. return nil
  86. }
  87. func (m IptcMap) Dump() []byte {
  88. buf := new(bytes.Buffer)
  89. for key, values := range m {
  90. for _, value := range values {
  91. dataSize := len(value.Raw)
  92. // Skip tags with too big data size
  93. if dataSize > math.MaxUint32 {
  94. continue
  95. }
  96. buf.WriteByte(iptcTagHeader)
  97. buf.WriteByte(key.RecordID)
  98. buf.WriteByte(key.TagID)
  99. if dataSize < (1 << 15) {
  100. binary.Write(buf, binary.BigEndian, uint16(dataSize))
  101. } else {
  102. binary.Write(buf, binary.BigEndian, uint16(4+(1<<15)))
  103. binary.Write(buf, binary.BigEndian, uint32(dataSize))
  104. }
  105. buf.Write(value.Raw)
  106. }
  107. }
  108. return buf.Bytes()
  109. }