From 3014bd741394332b59fc2b90babe2136308c929e Mon Sep 17 00:00:00 2001 From: mpl Date: Wed, 18 Apr 2018 03:46:57 +0200 Subject: [PATCH] pkg/index: read EXIF bytes when HEIC file Updates #969 Change-Id: I11cc1668b853fe5bc2d076addda1a60c08be1dc5 --- internal/images/images.go | 4 +++- .../testdata/black-seattle-truncated.heic | Bin 0 -> 10240 bytes pkg/index/indextest/tests.go | 9 ++++++++- pkg/index/receive.go | 9 +++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 pkg/index/indextest/testdata/black-seattle-truncated.heic diff --git a/internal/images/images.go b/internal/images/images.go index 1e00d756e..8795bb3c1 100644 --- a/internal/images/images.go +++ b/internal/images/images.go @@ -144,7 +144,8 @@ type DecodeOpts struct { type Config struct { Width, Height int Format string - Modified bool // true if Decode actually rotated or flipped the image. + Modified bool // true if Decode actually rotated or flipped the image. + HEICEXIF []byte // if not nil, the part of the HEIC file that contains EXIF metadata } func (c *Config) setBounds(im image.Image) { @@ -410,6 +411,7 @@ func DecodeConfig(r io.Reader) (Config, error) { if err != nil { return c, err } + c.HEICEXIF = exifBytes mr = bytes.NewReader(exifBytes) } diff --git a/pkg/index/indextest/testdata/black-seattle-truncated.heic b/pkg/index/indextest/testdata/black-seattle-truncated.heic new file mode 100644 index 0000000000000000000000000000000000000000..84ee1e4f0f3d80a73e02cc9d79034f62002a2a01 GIT binary patch literal 10240 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 literal 0 HcmV?d00001 diff --git a/pkg/index/indextest/tests.go b/pkg/index/indextest/tests.go index 166c899ed..1d4af20fd 100644 --- a/pkg/index/indextest/tests.go +++ b/pkg/index/indextest/tests.go @@ -342,7 +342,7 @@ func Index(t *testing.T, initIdx func() *index.Index) { } // Upload some files. - var jpegFileRef, exifFileRef, exifWholeRef, badExifWholeRef, nanExifWholeRef, mediaFileRef, mediaWholeRef blob.Ref + var jpegFileRef, exifFileRef, exifWholeRef, badExifWholeRef, nanExifWholeRef, mediaFileRef, mediaWholeRef, heicEXIFWholeRef blob.Ref { camliRootPath, err := osutil.GoPackagePath("perkeep.org") if err != nil { @@ -362,6 +362,7 @@ func Index(t *testing.T, initIdx func() *index.Index) { _, badExifWholeRef = uploadFile("bad-exif.jpg", time.Unix(1361248796, 0)) _, nanExifWholeRef = uploadFile("nan-exif.jpg", time.Unix(1361248796, 0)) mediaFileRef, mediaWholeRef = uploadFile("0s.mp3", noTime) + _, heicEXIFWholeRef = uploadFile("black-seattle-truncated.heic", time.Unix(1361248796, 0)) } // Upload the dir containing the previous files. @@ -411,6 +412,12 @@ func Index(t *testing.T, initIdx func() *index.Index) { t.Errorf("EXIF nan-exif.jpg key %q = %q; want %q", key, g, e) } + // Check that we can read EXIF from HEIC files too + key = "exifgps|" + heicEXIFWholeRef.String() + if g, e := id.Get(key), "47.6496056|-122.3512806"; g != e { + t.Errorf("EXIF black-seattle-truncated.heic key %q = %q; want %q", key, g, e) + } + key = "have:" + pn.String() pnSizeStr := strings.TrimSuffix(id.Get(key), "|indexed") if pnSizeStr == "" { diff --git a/pkg/index/receive.go b/pkg/index/receive.go index 753d8cacc..d73e39365 100644 --- a/pkg/index/receive.go +++ b/pkg/index/receive.go @@ -489,12 +489,17 @@ func (ix *Index) populateFile(ctx context.Context, fetcher blob.Fetcher, b *sche mm.Set(keyImageSize.Key(blobRef), keyImageSize.Val(fmt.Sprint(conf.Width), fmt.Sprint(conf.Height))) } + exifData := imageBuf.Bytes + if conf.HEICEXIF != nil { + exifData = conf.HEICEXIF + } var ft time.Time fileTime := func(r filePrefixReader) error { ft, err = schema.FileTime(r) return err } - if err = readPrefixOrFile(imageBuf.Bytes, fetcher, b, fileTime); err == nil { + + if err = readPrefixOrFile(exifData, fetcher, b, fileTime); err == nil { times = append(times, ft) } if exifDebug { @@ -505,7 +510,7 @@ func (ix *Index) populateFile(ctx context.Context, fetcher blob.Fetcher, b *sche indexEXIFData := func(r filePrefixReader) error { return indexEXIF(wholeRef, r, mm) } - if err = readPrefixOrFile(imageBuf.Bytes, fetcher, b, indexEXIFData); err != nil { + if err = readPrefixOrFile(exifData, fetcher, b, indexEXIFData); err != nil { if exifDebug { log.Printf("error parsing EXIF: %v", err) }