Skip to content

Commit afd7cfe

Browse files
committed
Add support for extracting EXIF data from PNG files.
Closes evanoberholster#41.
1 parent 6924485 commit afd7cfe

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

exif2/reader.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func Decode(r io.ReadSeeker) (Exif, error) {
3333
return ir.Exif, nil
3434
}
3535

36-
func (ir *ifdReader) DecodeTiff(r io.Reader, h meta.ExifHeader) error {
36+
func (ir *ifdReader) DecodeTiff(_ io.Reader, h meta.ExifHeader) error {
3737
ir.buffer.clear()
3838
// Log Header Info
3939
if ir.logInfo() {

png.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package imagemeta
2+
3+
import (
4+
"io"
5+
6+
"github.com/evanoberholster/imagemeta/exif2"
7+
"github.com/evanoberholster/imagemeta/png"
8+
)
9+
10+
// DecodePng decodes a PNG file from an io.Reader returning Exif or an error.
11+
func DecodePng(r io.ReadSeeker) (exif2.Exif, error) {
12+
header, err := png.ScanPngHeader(r)
13+
if err != nil {
14+
return exif2.Exif{}, err
15+
}
16+
17+
ir := exif2.NewIfdReader(r)
18+
defer ir.Close()
19+
20+
if err := ir.DecodeTiff(nil, header); err != nil {
21+
return ir.Exif, err
22+
}
23+
24+
return ir.Exif, nil
25+
}

png/png.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Package png reads PNG Header metadata information from image files before being processed by exif package
2+
package png
3+
4+
import (
5+
"encoding/binary"
6+
"io"
7+
8+
"github.com/evanoberholster/imagemeta/imagetype"
9+
"github.com/evanoberholster/imagemeta/meta"
10+
)
11+
12+
func ScanPngHeader(r io.ReadSeeker) (header meta.ExifHeader, err error) {
13+
// 5.2 PNG signature
14+
const signature = "\x89PNG\r\n\x1a\n"
15+
16+
// 5.3 Chunk layout
17+
const crcSize = 4
18+
19+
// 8 is the size of both the signature and the chunk
20+
// id (4 bytes) + chunk length (4 bytes).
21+
// This is just a coincidence.
22+
buf := make([]byte, 8)
23+
24+
var n int
25+
n, err = r.Read(buf)
26+
if err != nil {
27+
return
28+
}
29+
30+
if n != len(signature) || string(buf) != signature {
31+
err = meta.ErrNoExif
32+
33+
return
34+
}
35+
36+
for {
37+
// 5.3 Chunk layout
38+
n, err = r.Read(buf)
39+
if err != nil {
40+
break
41+
}
42+
43+
if n != len(buf) {
44+
break
45+
}
46+
47+
length := binary.BigEndian.Uint32(buf[0:4])
48+
chunkType := string(buf[4:8])
49+
50+
switch chunkType {
51+
case "eXIf":
52+
offset, _ := r.Seek(0, io.SeekCurrent)
53+
54+
return meta.NewExifHeader(meta.BigEndian, 8, uint32(offset), length, imagetype.ImagePNG), nil
55+
56+
default:
57+
// Discard the chunk length + CRC.
58+
_, err := r.Seek(int64(length+crcSize), io.SeekCurrent)
59+
if err != nil {
60+
return header, err
61+
}
62+
}
63+
}
64+
65+
return header, meta.ErrNoExif
66+
}

0 commit comments

Comments
 (0)