diff --git a/gen/main.go b/gen/main.go index 7de1a11..07282a3 100644 --- a/gen/main.go +++ b/gen/main.go @@ -18,7 +18,7 @@ func main() { } base := "../testdata" - if err := filepath.Walk(base, func(path string, info os.FileInfo, err error) error { + if err := filepath.Walk(filepath.Join(base, "images"), func(path string, info os.FileInfo, err error) error { if err != nil { return err } diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/11b4836f396ea129.json b/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/11b4836f396ea129.json deleted file mode 100644 index c7a3de9..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/11b4836f396ea129.json +++ /dev/null @@ -1,20 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodeJPG/11b4836f396ea129", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "11b4836f396ea129", - "Directory": "../testdata/fuzz/FuzzDecodeJPG", - "FileSize": 255743, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "utf-8", - "ByteOrderMark": 0, - "Newlines": "\n", - "LineCount": 2, - "WordCount": 1152 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/1ccacf70a7af5b48.json b/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/1ccacf70a7af5b48.json deleted file mode 100644 index a4ed9d2..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/1ccacf70a7af5b48.json +++ /dev/null @@ -1,20 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodeJPG/1ccacf70a7af5b48", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "1ccacf70a7af5b48", - "Directory": "../testdata/fuzz/FuzzDecodeJPG", - "FileSize": 333742, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "utf-8", - "ByteOrderMark": 0, - "Newlines": "\n", - "LineCount": 2, - "WordCount": 1729 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/9c46786602e37dac.json b/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/9c46786602e37dac.json deleted file mode 100644 index 2c99c15..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/9c46786602e37dac.json +++ /dev/null @@ -1,20 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodeJPG/9c46786602e37dac", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "9c46786602e37dac", - "Directory": "../testdata/fuzz/FuzzDecodeJPG", - "FileSize": 172821, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "utf-8", - "ByteOrderMark": 0, - "Newlines": "\n", - "LineCount": 2, - "WordCount": 261 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/be5ae98210f88f5a.json b/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/be5ae98210f88f5a.json deleted file mode 100644 index bc71799..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/be5ae98210f88f5a.json +++ /dev/null @@ -1,20 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodeJPG/be5ae98210f88f5a", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "be5ae98210f88f5a", - "Directory": "../testdata/fuzz/FuzzDecodeJPG", - "FileSize": 57371, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "utf-8", - "ByteOrderMark": 0, - "Newlines": "\n", - "LineCount": 2, - "WordCount": 100 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/c6c20bc8145eb1ca.json b/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/c6c20bc8145eb1ca.json deleted file mode 100644 index 2df9580..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodeJPG/c6c20bc8145eb1ca.json +++ /dev/null @@ -1,20 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodeJPG/c6c20bc8145eb1ca", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "c6c20bc8145eb1ca", - "Directory": "../testdata/fuzz/FuzzDecodeJPG", - "FileSize": 57545, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "utf-8", - "ByteOrderMark": 0, - "Newlines": "\n", - "LineCount": 2, - "WordCount": 102 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/1d4b3e6d8e083b46.json b/gen/testdata_exiftool/fuzz/FuzzDecodePNG/1d4b3e6d8e083b46.json deleted file mode 100644 index 9455708..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/1d4b3e6d8e083b46.json +++ /dev/null @@ -1,19 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodePNG/1d4b3e6d8e083b46", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "1d4b3e6d8e083b46", - "Directory": "../testdata/fuzz/FuzzDecodePNG", - "FileSize": 317, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "us-ascii", - "Newlines": "\n", - "LineCount": 2, - "WordCount": 9 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/c99a254e79433054.json b/gen/testdata_exiftool/fuzz/FuzzDecodePNG/c99a254e79433054.json deleted file mode 100644 index 1017035..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/c99a254e79433054.json +++ /dev/null @@ -1,19 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodePNG/c99a254e79433054", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "c99a254e79433054", - "Directory": "../testdata/fuzz/FuzzDecodePNG", - "FileSize": 314, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "us-ascii", - "Newlines": "\n", - "LineCount": 2, - "WordCount": 8 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/cd66d0b7410fd5c5.json b/gen/testdata_exiftool/fuzz/FuzzDecodePNG/cd66d0b7410fd5c5.json deleted file mode 100644 index 30c5351..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/cd66d0b7410fd5c5.json +++ /dev/null @@ -1,19 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodePNG/cd66d0b7410fd5c5", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "cd66d0b7410fd5c5", - "Directory": "../testdata/fuzz/FuzzDecodePNG", - "FileSize": 64, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "us-ascii", - "Newlines": "\n", - "LineCount": 2, - "WordCount": 8 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/e24f658a5bbdb019.json b/gen/testdata_exiftool/fuzz/FuzzDecodePNG/e24f658a5bbdb019.json deleted file mode 100644 index 8b91e6a..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodePNG/e24f658a5bbdb019.json +++ /dev/null @@ -1,19 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodePNG/e24f658a5bbdb019", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "e24f658a5bbdb019", - "Directory": "../testdata/fuzz/FuzzDecodePNG", - "FileSize": 320, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "us-ascii", - "Newlines": "\n", - "LineCount": 2, - "WordCount": 8 - } -}] diff --git a/gen/testdata_exiftool/fuzz/FuzzDecodeWebP/cd8c6f6742f1c6a7.json b/gen/testdata_exiftool/fuzz/FuzzDecodeWebP/cd8c6f6742f1c6a7.json deleted file mode 100644 index d50d063..0000000 --- a/gen/testdata_exiftool/fuzz/FuzzDecodeWebP/cd8c6f6742f1c6a7.json +++ /dev/null @@ -1,20 +0,0 @@ -[{ - "SourceFile": "../testdata/fuzz/FuzzDecodeWebP/cd8c6f6742f1c6a7", - "ExifTool": { - "ExifToolVersion": 12.76 - }, - "File": { - "FileName": "cd8c6f6742f1c6a7", - "Directory": "../testdata/fuzz/FuzzDecodeWebP", - "FileSize": 212890, - "FilePermissions": 100644, - "FileType": "TXT", - "FileTypeExtension": "TXT", - "MIMEType": "text/plain", - "MIMEEncoding": "utf-8", - "ByteOrderMark": 0, - "Newlines": "\n", - "LineCount": 2, - "WordCount": 1403 - } -}] diff --git a/gen/testdata_exiftool/images/huge_tag_exif.jpg.json b/gen/testdata_exiftool/images/corrupt/huge_tag_exif.jpg.json similarity index 78% rename from gen/testdata_exiftool/images/huge_tag_exif.jpg.json rename to gen/testdata_exiftool/images/corrupt/huge_tag_exif.jpg.json index 8c39535..f120df2 100644 --- a/gen/testdata_exiftool/images/huge_tag_exif.jpg.json +++ b/gen/testdata_exiftool/images/corrupt/huge_tag_exif.jpg.json @@ -1,12 +1,12 @@ [{ - "SourceFile": "../testdata/images/huge_tag_exif.jpg", + "SourceFile": "../testdata/images/corrupt/huge_tag_exif.jpg", "ExifTool": { "ExifToolVersion": 12.76, "Warning": "JPEG format error" }, "File": { "FileName": "huge_tag_exif.jpg", - "Directory": "../testdata/images", + "Directory": "../testdata/images/corrupt", "FileSize": 65536, "FilePermissions": 100644, "FileType": "JPEG", diff --git a/gen/testdata_exiftool/images/infinite_loop_exif.jpg.json b/gen/testdata_exiftool/images/corrupt/infinite_loop_exif.jpg.json similarity index 95% rename from gen/testdata_exiftool/images/infinite_loop_exif.jpg.json rename to gen/testdata_exiftool/images/corrupt/infinite_loop_exif.jpg.json index 7b565da..4297291 100644 --- a/gen/testdata_exiftool/images/infinite_loop_exif.jpg.json +++ b/gen/testdata_exiftool/images/corrupt/infinite_loop_exif.jpg.json @@ -1,12 +1,12 @@ [{ - "SourceFile": "../testdata/images/infinite_loop_exif.jpg", + "SourceFile": "../testdata/images/corrupt/infinite_loop_exif.jpg", "ExifTool": { "ExifToolVersion": 12.76, "Warning": "Suspicious IFD0 offset for XResolution" }, "File": { "FileName": "infinite_loop_exif.jpg", - "Directory": "../testdata/images", + "Directory": "../testdata/images/corrupt", "FileSize": 3738, "FilePermissions": 100644, "FileType": "JPEG", diff --git a/gen/testdata_exiftool/images/max_uint32_exif.jpg.json b/gen/testdata_exiftool/images/corrupt/max_uint32_exif.jpg.json similarity index 77% rename from gen/testdata_exiftool/images/max_uint32_exif.jpg.json rename to gen/testdata_exiftool/images/corrupt/max_uint32_exif.jpg.json index 1fc3680..fc371da 100644 --- a/gen/testdata_exiftool/images/max_uint32_exif.jpg.json +++ b/gen/testdata_exiftool/images/corrupt/max_uint32_exif.jpg.json @@ -1,12 +1,12 @@ [{ - "SourceFile": "../testdata/images/max_uint32_exif.jpg", + "SourceFile": "../testdata/images/corrupt/max_uint32_exif.jpg", "ExifTool": { "ExifToolVersion": 12.76, "Warning": "JPEG format error" }, "File": { "FileName": "max_uint32_exif.jpg", - "Directory": "../testdata/images", + "Directory": "../testdata/images/corrupt", "FileSize": 65536, "FilePermissions": 100644, "FileType": "JPEG", diff --git a/gen/testdata_exiftool/images/hugo-issue-12669.jpg.json b/gen/testdata_exiftool/images/hugo-issue-12669.jpg.json new file mode 100644 index 0000000..a88a34a --- /dev/null +++ b/gen/testdata_exiftool/images/hugo-issue-12669.jpg.json @@ -0,0 +1,87 @@ +[{ + "SourceFile": "../testdata/images/hugo-issue-12669.jpg", + "ExifTool": { + "ExifToolVersion": 12.76 + }, + "File": { + "FileName": "hugo-issue-12669.jpg", + "Directory": "../testdata/images", + "FileSize": 5772106, + "FilePermissions": 100644, + "FileType": "JPEG", + "FileTypeExtension": "JPG", + "MIMEType": "image/jpeg", + "ExifByteOrder": "II", + "ImageWidth": 4032, + "ImageHeight": 2268, + "EncodingProcess": 0, + "BitsPerSample": 8, + "ColorComponents": 3, + "YCbCrSubSampling": "2 2" + }, + "EXIF": { + "ImageWidth": 4032, + "ImageHeight": 2268, + "Make": "samsung", + "Model": "SM-G950U", + "Orientation": 1, + "XResolution": 72, + "YResolution": 72, + "ResolutionUnit": 2, + "Software": "G950USQS8DTJ1", + "ModifyDate": "2021:01:17 11:34:47", + "YCbCrPositioning": 1, + "ExposureTime": 0.0003172588832, + "FNumber": 1.7, + "ExposureProgram": 2, + "ISO": 50, + "ExifVersion": "0220", + "DateTimeOriginal": "2021:01:17 11:34:47", + "CreateDate": "2021:01:17 11:34:47", + "ComponentsConfiguration": "1 2 3 0", + "ShutterSpeedValue": "0.000317270278919785", + "ApertureValue": 1.6993699982773, + "BrightnessValue": 8.98, + "ExposureCompensation": 0, + "MaxApertureValue": 1.6993699982773, + "MeteringMode": 2, + "LightSource": 0, + "Flash": 0, + "FocalLength": 4.25, + "UserComment": "\n", + "FlashpixVersion": "0100", + "ColorSpace": 1, + "ExifImageWidth": 4032, + "ExifImageHeight": 2268, + "InteropIndex": "R98", + "InteropVersion": "0100", + "SensingMethod": 2, + "SceneType": 1, + "ExposureMode": 0, + "WhiteBalance": 0, + "FocalLengthIn35mmFormat": 26, + "SceneCaptureType": 0, + "ImageUniqueID": "F12QSJA00SM F12QSKB01SB", + "GPSVersionID": "2 2 0 0", + "GPSLatitudeRef": "N", + "GPSLatitude": 0, + "GPSLongitudeRef": "E", + "GPSLongitude": 0, + "GPSAltitudeRef": 0, + "GPSAltitude": 0, + "GPSTimeStamp": "18:34:47", + "GPSDateStamp": "2021:01:17", + "Compression": 6, + "ThumbnailOffset": 6312, + "ThumbnailLength": 16863 + }, + "MakerNotes": { + "MakerNoteVersion": "0100", + "DeviceType": 73728, + "RawDataByteOrder": 0, + "RawDataCFAPattern": 1, + "FaceDetect": 0, + "TimeStamp": "2021:01:17 19:34:47.374+01:00", + "MCCData": 310 + } +}] diff --git a/helpers.go b/helpers.go index e1dde3c..c301ee4 100644 --- a/helpers.go +++ b/helpers.go @@ -276,6 +276,31 @@ func (vc) convertStringToInt(ctx valueConverterContext, v any) any { return i } +func (c vc) convertUserComment(ctx valueConverterContext, v any) any { + // UserComment tag is identified based on an ID code in a fixed 8-byte area at the start of the tag data area. + b, ok := typeAssert[[]byte](ctx, v) + if !ok { + return "" + } + if len(b) < 8 { + return "" + } + id := string(b[:8]) + + switch id { + case "ASCII\x00\x00\x00": + s := printableString(string(trimBytesNulls(b[8:]))) + if !isASCII(s) { + return "" + } + return s + case "UNICODE\x00": + return printableString(string(trimBytesNulls(b[8:]))) + default: + return "" + } +} + func (vc) ratNum(v any) any { switch vv := v.(type) { case Rat[uint32]: @@ -357,6 +382,15 @@ func (c vc) toDegrees(v any) (float64, error) { } } +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > unicode.MaxASCII { + return false + } + } + return true +} + func printableString(s string) string { ss := strings.Map(func(r rune) rune { if unicode.IsGraphic(r) { diff --git a/imagemeta_test.go b/imagemeta_test.go index ce8f7e7..1241d38 100644 --- a/imagemeta_test.go +++ b/imagemeta_test.go @@ -384,6 +384,10 @@ func TestDecodeErrors(t *testing.T) { c.Assert(imagemeta.Decode(imagemeta.Options{R: strings.NewReader("foo"), ImageFormat: imagemeta.ImageFormat(1234)}), qt.ErrorMatches, "unsupported image format") } +func TestGoldenEXIFHugoIssue12669(t *testing.T) { + compareWithExiftoolOutput(t, "hugo-issue-12669.jpg", imagemeta.EXIF) +} + func TestGoldenEXIF(t *testing.T) { withGolden(t, imagemeta.EXIF) } diff --git a/metadecoder_exif.go b/metadecoder_exif.go index 0a8ffb9..1265d55 100644 --- a/metadecoder_exif.go +++ b/metadecoder_exif.go @@ -98,9 +98,7 @@ var ( "ComponentsConfiguration": exifConverters.convertBytesToStringSpaceDelim, "LensInfo": exifConverters.convertRatsToSpaceLimited, "Padding": exifConverters.convertBinaryData, - "UserComment": func(ctx valueConverterContext, v any) any { - return strings.TrimPrefix(printableString(toString(v)), "ASCII") - }, + "UserComment": exifConverters.convertUserComment, "CFAPattern": func(ctx valueConverterContext, v any) any { b := v.([]byte) horizontalRepeat := ctx.s.byteOrder.Uint16(b[:2]) diff --git a/testdata/images/hugo-issue-12669.jpg b/testdata/images/hugo-issue-12669.jpg new file mode 100644 index 0000000..c9c2545 Binary files /dev/null and b/testdata/images/hugo-issue-12669.jpg differ