├── go.mod ├── test_data ├── f1t.jpg ├── f2t.jpg ├── f3t.jpg ├── f4t.jpg ├── f5t.jpg ├── f6t.jpg ├── f7t.jpg └── f8t.jpg ├── README.md ├── LICENSE ├── decode_test.go └── decode.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/edwvee/exiffix 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /test_data/f1t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f1t.jpg -------------------------------------------------------------------------------- /test_data/f2t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f2t.jpg -------------------------------------------------------------------------------- /test_data/f3t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f3t.jpg -------------------------------------------------------------------------------- /test_data/f4t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f4t.jpg -------------------------------------------------------------------------------- /test_data/f5t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f5t.jpg -------------------------------------------------------------------------------- /test_data/f6t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f6t.jpg -------------------------------------------------------------------------------- /test_data/f7t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f7t.jpg -------------------------------------------------------------------------------- /test_data/f8t.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwvee/exiffix/HEAD/test_data/f8t.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Exiffix 2 | ====== 3 | Exiffix is a one function golang library made to be a replacement for image.Decode to handle orientation stored in EXIF data. 4 | 5 | ```exiffix.Decode``` has mostly the same signature as ```image.Decode```. The difference is that it requires ```io.ReadSeeker``` instead of ```io.Seeker```. 6 | 7 | Example 8 | ====== 9 | ```go 10 | file, err := os.Open(path) 11 | if err != nil{ 12 | panic(err) 13 | } 14 | defer file.Close() 15 | img, fmt, err := exiffix.Decode(file) 16 | ``` 17 | 18 | Special thanks to Macilias: part of code is taken from his [repo](https://github.com/Macilias/go-images-orientation) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 edwvee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /decode_test.go: -------------------------------------------------------------------------------- 1 | package exiffix 2 | 3 | import ( 4 | "image/color" 5 | "os" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | func TestDecode(t *testing.T) { 11 | for i := 1; i < 9; i++ { 12 | path := "test_data/f" + strconv.Itoa(i) + "t.jpg" 13 | file, err := os.Open(path) 14 | if err != nil { 15 | panic(err) 16 | } 17 | defer file.Close() 18 | img, _, err := Decode(file) 19 | if err != nil { 20 | panic(err) 21 | } 22 | bounds := img.Bounds() 23 | minP := bounds.Min 24 | maxP := bounds.Max 25 | leftUpper := img.At(minP.X, minP.Y) 26 | rightUpper := img.At(maxP.X-1, minP.Y) 27 | leftLower := img.At(minP.X, maxP.Y-1) 28 | rightLower := img.At(maxP.X-1, maxP.Y-1) 29 | if !(isBlack(leftUpper) && isBlack(rightUpper) && isBlack(leftLower) && !isBlack(rightLower)) { 30 | t.Errorf(` 31 | The path is %s. 32 | isBlack(leftUpper) && isBlack(rightUpper) && isBlack(leftLower) && !isBlack(rightLower): 33 | Expected %t, %t, %t, %t. 34 | Got %t, %t, %t, %t`, 35 | path, 36 | true, true, true, true, 37 | isBlack(leftUpper), isBlack(rightUpper), isBlack(leftLower), !isBlack(rightLower), 38 | ) 39 | } 40 | } 41 | } 42 | 43 | func isBlack(point color.Color) bool { 44 | r, g, b, _ := point.RGBA() 45 | return r == 0 && g == 0 && b == 0 46 | } 47 | -------------------------------------------------------------------------------- /decode.go: -------------------------------------------------------------------------------- 1 | package exiffix 2 | 3 | import ( 4 | "image" 5 | "io" 6 | 7 | "github.com/disintegration/imaging" 8 | "github.com/rwcarlsen/goexif/exif" 9 | ) 10 | 11 | //Decode is image.Decode handling orientation in EXIF tags if exists. 12 | //Requires io.ReadSeeker instead of io.Reader. 13 | func Decode(reader io.ReadSeeker) (image.Image, string, error) { 14 | img, fmt, err := image.Decode(reader) 15 | if err != nil { 16 | return img, fmt, err 17 | } 18 | reader.Seek(0, io.SeekStart) 19 | orientation := getOrientation(reader) 20 | switch orientation { 21 | case "1": 22 | case "2": 23 | img = imaging.FlipH(img) 24 | case "3": 25 | img = imaging.Rotate180(img) 26 | case "4": 27 | img = imaging.Rotate180(imaging.FlipH(img)) 28 | case "5": 29 | img = imaging.Rotate270(imaging.FlipV(img)) 30 | case "6": 31 | img = imaging.Rotate270(img) 32 | case "7": 33 | img = imaging.Rotate90(imaging.FlipV(img)) 34 | case "8": 35 | img = imaging.Rotate90(img) 36 | } 37 | 38 | return img, fmt, err 39 | } 40 | 41 | func getOrientation(reader io.Reader) string { 42 | x, err := exif.Decode(reader) 43 | if err != nil { 44 | return "1" 45 | } 46 | if x != nil { 47 | orient, err := x.Get(exif.Orientation) 48 | if err != nil { 49 | return "1" 50 | } 51 | if orient != nil { 52 | return orient.String() 53 | } 54 | } 55 | 56 | return "1" 57 | } 58 | --------------------------------------------------------------------------------