| 1 | package main |
| 2 | |
| 3 | import "encoding/binary" |
| 4 | |
| 5 | const exifPrefix = "Exif\x00\x00" |
| 6 | |
| 7 | func extractExif(orig []byte) []byte { |
| 8 | if len(orig) < 4 || orig[0] != 0xff || orig[1] != 0xd8 { |
| 9 | return nil |
| 10 | } |
| 11 | pos := 2 |
| 12 | for pos+4 <= len(orig) { |
| 13 | if orig[pos] != 0xff { |
| 14 | return nil |
| 15 | } |
| 16 | marker := orig[pos+1] |
| 17 | pos += 2 |
| 18 | if marker == 0xda || marker == 0xd9 { // SOS or EOI |
| 19 | return nil |
| 20 | } |
| 21 | if marker >= 0xd0 && marker <= 0xd7 { // restart markers have no length |
| 22 | continue |
| 23 | } |
| 24 | if pos+2 > len(orig) { |
| 25 | return nil |
| 26 | } |
| 27 | segLen := int(binary.BigEndian.Uint16(orig[pos : pos+2])) |
| 28 | if segLen < 2 || pos+segLen > len(orig) { |
| 29 | return nil |
| 30 | } |
| 31 | payload := orig[pos+2 : pos+segLen] |
| 32 | if marker == 0xe1 && len(payload) >= len(exifPrefix) && string(payload[:len(exifPrefix)]) == exifPrefix { |
| 33 | exif := make([]byte, len(payload)-len(exifPrefix)) |
| 34 | copy(exif, payload[len(exifPrefix):]) |
| 35 | return exif |
| 36 | } |
| 37 | pos += segLen |
| 38 | } |
| 39 | return nil |
| 40 | } |
| 41 | |
| 42 | func stripGPS(exif []byte) []byte { |
| 43 | if len(exif) < 8 { |
| 44 | return exif |
| 45 | } |
| 46 | out := make([]byte, len(exif)) |
| 47 | copy(out, exif) |
| 48 | |
| 49 | var order binary.ByteOrder |
| 50 | switch string(out[:2]) { |
| 51 | case "II": |
| 52 | order = binary.LittleEndian |
| 53 | case "MM": |
| 54 | order = binary.BigEndian |
| 55 | default: |
| 56 | return out |
| 57 | } |
| 58 | if order.Uint16(out[2:4]) != 42 { |
| 59 | return out |
| 60 | } |
| 61 | ifd0 := int(order.Uint32(out[4:8])) |
| 62 | if ifd0 < 0 || ifd0+2 > len(out) { |
| 63 | return out |
| 64 | } |
| 65 | count := int(order.Uint16(out[ifd0 : ifd0+2])) |
| 66 | entries := ifd0 + 2 |
| 67 | for i := 0; i < count; i++ { |
| 68 | entry := entries + i*12 |
| 69 | if entry+12 > len(out) { |
| 70 | return out |
| 71 | } |
| 72 | tag := order.Uint16(out[entry : entry+2]) |
| 73 | if tag == 0x8825 { // GPSInfo IFDPointer |
| 74 | gpsOffset := int(order.Uint32(out[entry+8 : entry+12])) |
| 75 | if gpsOffset >= 0 && gpsOffset+2 <= len(out) { |
| 76 | order.PutUint16(out[gpsOffset:gpsOffset+2], 0) |
| 77 | } |
| 78 | return out |
| 79 | } |
| 80 | } |
| 81 | return out |
| 82 | } |
| 83 | |
| 84 | func injectExif(jpegBytes, exif []byte) []byte { |
| 85 | if len(exif) == 0 || len(jpegBytes) < 2 || jpegBytes[0] != 0xff || jpegBytes[1] != 0xd8 { |
| 86 | return jpegBytes |
| 87 | } |
| 88 | payloadLen := len(exifPrefix) + len(exif) |
| 89 | segLen := payloadLen + 2 |
| 90 | if segLen > 0xffff { |
| 91 | return jpegBytes |
| 92 | } |
| 93 | out := make([]byte, 0, len(jpegBytes)+segLen+2) |
| 94 | out = append(out, jpegBytes[:2]...) |
| 95 | out = append(out, 0xff, 0xe1, byte(segLen>>8), byte(segLen)) |
| 96 | out = append(out, exifPrefix...) |
| 97 | out = append(out, exif...) |
| 98 | out = append(out, jpegBytes[2:]...) |
| 99 | return out |
| 100 | } |