aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Horan <[email protected]>2021-11-30 23:09:32 -0500
committerAlexander Rakoczy <[email protected]>2022-01-29 15:01:00 -0500
commit89e98276d47d2df0fd8546857944121b4b742b68 (patch)
treed8c3119b91e44056d74531a9cafa7a42e18f32d4
parent4a6e64b918d7bdb77ee42c5f14ea7663e5802d08 (diff)
Rotate JPEG images based on the Exif Orientation
Signed-off-by: Alexander Rakoczy <[email protected]>
-rw-r--r--cmd/server/main.go82
-rw-r--r--go.mod2
-rw-r--r--go.sum5
3 files changed, 57 insertions, 32 deletions
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 41dbce0..335ef90 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -8,10 +8,10 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
"errors"
"flag"
"fmt"
+ "image/jpeg"
"io"
"io/fs"
"io/ioutil"
@@ -32,6 +32,7 @@ import (
"cloud.google.com/go/secretmanager/apiv1"
"cloud.google.com/go/storage"
"git.toothrot.net/i.dis.band/internal"
+ "github.com/disintegration/imageorient"
secretmanager2 "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
)
@@ -115,8 +116,7 @@ const (
const maxUpload = 100 * MiB
type exifTag struct {
- SourceFile string
- Orientation string
+ SourceFile string
}
func upload(b *storage.BucketHandle) http.Handler {
@@ -171,22 +171,63 @@ func upload(b *storage.BucketHandle) http.Handler {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
+ if _, err := file.Seek(0, io.SeekStart); err != nil {
+ log.Printf("file.Seek(0, io.SeekStart) = _, %v", err)
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ buff := make([]byte, 512)
+ bytesRead, err := file.Read(buff)
+ if err != nil {
+ log.Printf("io.Read(%v) = %v, %v", buff, bytesRead, err)
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ dct := http.DetectContentType(buff[:bytesRead])
tmp.Close()
file.Close()
r.MultipartForm.RemoveAll()
- or, err := orientation(r.Context(), tmp.Name())
+ out, err := commandContext(r.Context(), "exiftool", "-icc_profile", "-b", "-w", "icc", tmp.Name())
if err != nil {
- log.Printf("orientation(%q) = %v, %v", tmp.Name(), or, err)
+ log.Printf("commandContext(): %v, %q", err, string(out))
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
}
- out, err := commandContext(r.Context(), "exiftool", "-all=", "--icc_profile:all", "-overwrite_original_in_place", tmp.Name())
+ if dct == "image/jpeg" {
+ f, err := os.Open(tmp.Name())
+ if err != nil {
+ log.Printf("os.Open(%v) = %v, %v", tmp.Name(), f, err)
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+
+ img, _, err := imageorient.Decode(f)
+ if err != nil {
+ log.Printf("imageorient.Decode(%v) = %v, _, %v", f, img, err)
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+
+ f, err = os.OpenFile(tmp.Name(), os.O_WRONLY|os.O_TRUNC, 0600)
+ if err != nil {
+ log.Printf("os.OpenFile(%v, os.O_WRONLY|os.O_TRUNC, 0600) = %v, %v", tmp.Name(), f, err)
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ err = jpeg.Encode(f, img, nil)
+ if err != nil {
+ log.Printf("jpeg.Encode(%v, %v, nil) = %v", f, img, err)
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ f.Close()
+ }
+ out, err = commandContext(r.Context(), "exiftool", "-all=", "-icc_profile<=%d%f.icc", "-overwrite_original_in_place", tmp.Name())
if err != nil {
log.Printf("commandContext(): %v, %q", err, string(out))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- if err := setOrientation(r.Context(), tmp.Name(), or); err != nil {
- log.Printf("setOrientation(%q) = %v", tmp.Name(), err)
- }
ct := head.Header.Get("content-type")
ext, err := mime.ExtensionsByType(ct)
@@ -236,29 +277,6 @@ func commandContext(ctx context.Context, name string, arg ...string) ([]byte, er
return stdOutErr.Bytes(), nil
}
-func orientation(ctx context.Context, filename string) (string, error) {
- o, err := commandContext(ctx, "exiftool", "-j", "-Orientation", filename)
- if err != nil {
- return "", err
- }
- var tags []exifTag
- if err := json.Unmarshal(o, &tags); err != nil || len(tags) == 0 {
- return "", err
- }
- log.Println(tags)
- return tags[0].Orientation, nil
-}
-
-func setOrientation(ctx context.Context, filename string, or string) error {
- args := []string{fmt.Sprintf("-Orientation=%s", or), "-overwrite_original_in_place", filename}
- out, err := commandContext(ctx, "exiftool", args...)
- if err != nil {
- log.Printf("setOrientation: %v, %+v, %q", err, args, out)
- return err
- }
- return nil
-}
-
func filename() string {
b := make([]byte, 8)
n, err := rand.Read(b)
diff --git a/go.mod b/go.mod
index 9229d00..4a311f6 100644
--- a/go.mod
+++ b/go.mod
@@ -10,6 +10,8 @@ require (
)
require (
+ github.com/disintegration/gift v1.2.1 // indirect
+ github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
diff --git a/go.sum b/go.sum
index 8aaff36..8f79574 100644
--- a/go.sum
+++ b/go.sum
@@ -63,6 +63,11 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/disintegration/gift v1.1.2/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
+github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
+github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
+github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec h1:YrB6aVr9touOt75I9O1SiancmR2GMg45U9UYf0gtgWg=
+github.com/disintegration/imageorient v0.0.0-20180920195336-8147d86e83ec/go.mod h1:K0KBFIr1gWu/C1Gp10nFAcAE4hsB7JxE6OgLijrJ8Sk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=