github.com/go-oss/image@v0.1.1-0.20230517025328-001b78555e78/imageutil/exif.go (about)

     1  package imageutil
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"image"
     8  	"image/jpeg"
     9  	"io"
    10  
    11  	"github.com/disintegration/imaging"
    12  	"github.com/rwcarlsen/goexif/exif"
    13  )
    14  
    15  // RemoveExif remove JPEG EXIF metadata and rotate image for orientation tag.
    16  func RemoveExif(r io.Reader) (io.Reader, error) {
    17  	buff := new(bytes.Buffer)
    18  	defer buff.Reset()
    19  	r = io.TeeReader(r, buff)
    20  
    21  	img, err := jpeg.Decode(r)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  
    26  	// check EXIF and apply orientation
    27  	meta, err := exif.Decode(buff)
    28  	if err == nil {
    29  		// エラーが発生した場合は入力画像の EXIF が不正なだけなので無視する
    30  		newImg, _ := applyOrientation(img, meta)
    31  		if newImg != nil {
    32  			img = newImg
    33  		}
    34  	}
    35  
    36  	// Remove EXIF metadata
    37  	reader, writer := io.Pipe()
    38  	go func() {
    39  		var err error
    40  		defer func() {
    41  			if cause := recover(); cause != nil && err == nil {
    42  				err = fmt.Errorf("panic: %+v", cause)
    43  			}
    44  			writer.CloseWithError(err)
    45  		}()
    46  		err = jpeg.Encode(writer, img, &jpeg.Options{Quality: 100})
    47  	}()
    48  	return reader, nil
    49  }
    50  
    51  func applyOrientation(img image.Image, meta *exif.Exif) (image.Image, error) {
    52  	tag, err := meta.Get(exif.Orientation)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	orientation, err := tag.Int(0)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	if orientation < 1 || orientation > 8 {
    61  		return nil, errors.New("invalid orientation")
    62  	}
    63  
    64  	// ref: http://www.exif.org/Exif2-2.PDF
    65  	// ref: http://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf
    66  	switch orientation {
    67  	case 1: // The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
    68  		return img, nil
    69  	case 2: // The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
    70  		img = imaging.FlipH(img)
    71  	case 3: // The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
    72  		img = imaging.FlipH(img)
    73  		img = imaging.FlipV(img)
    74  	case 4: // The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
    75  		img = imaging.FlipV(img)
    76  	case 5: // The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
    77  		img = imaging.Rotate270(img)
    78  		img = imaging.FlipH(img)
    79  	case 6: // The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
    80  		img = imaging.Rotate270(img)
    81  	case 7: // The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
    82  		img = imaging.Rotate90(img)
    83  		img = imaging.FlipH(img)
    84  	case 8: // The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
    85  		img = imaging.Rotate90(img)
    86  	}
    87  
    88  	return img, nil
    89  }