diff --git a/CHANGELOG.md b/CHANGELOG.md index a44cbb00..cc0a5d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Azure Blob Storage support. - `IMGPROXY_STRIP_COLOR_PROFILE` config and [strip_color_profile](https://docs.imgproxy.net/#/generating_the_url_advanced?id=strip-color-profile) processing option. - `IMGPROXY_FORMAT_QUALITY` config. +- `IMGPROXY_AUTO_ROTATE` config and [auto_rotate](https://docs.imgproxy.net/#/generating_the_url_advanced?id=auto-rotate) processing option. - (pro) Remove Adobe Illustrator garbage from SVGs. ### Changed diff --git a/config.go b/config.go index 06a8f3df..1c4e4db2 100644 --- a/config.go +++ b/config.go @@ -229,6 +229,7 @@ type config struct { GZipCompression int StripMetadata bool StripColorProfile bool + AutoRotate bool EnableWebpDetection bool EnforceWebp bool @@ -323,6 +324,7 @@ var conf = config{ FormatQuality: map[imageType]int{imageTypeAVIF: 50}, StripMetadata: true, StripColorProfile: true, + AutoRotate: true, UserAgent: fmt.Sprintf("imgproxy/%s", version), Presets: make(presets), WatermarkOpacity: 1, @@ -383,6 +385,7 @@ func configure() error { intEnvConfig(&conf.GZipCompression, "IMGPROXY_GZIP_COMPRESSION") boolEnvConfig(&conf.StripMetadata, "IMGPROXY_STRIP_METADATA") boolEnvConfig(&conf.StripColorProfile, "IMGPROXY_STRIP_COLOR_PROFILE") + boolEnvConfig(&conf.AutoRotate, "IMGPROXY_AUTO_ROTATE") boolEnvConfig(&conf.EnableWebpDetection, "IMGPROXY_ENABLE_WEBP_DETECTION") boolEnvConfig(&conf.EnforceWebp, "IMGPROXY_ENFORCE_WEBP") diff --git a/docs/configuration.md b/docs/configuration.md index d7f11e46..6ffa4a9f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -325,3 +325,4 @@ imgproxy can send logs to syslog, but this feature is disabled by default. To en * `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEG and WebP. Allows to process the whole image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images. * `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true`. * `IMGPROXY_STRIP_COLOR_PROFILE`: when `true`, imgproxy will transform the embedded color profile (ICC) to sRGB and remove it from the image. Otherwise, imgproxy will try to keep it as is. Default: `true`. +* `IMGPROXY_AUTO_ROTATE`: when `true`, imgproxy will auto rotate images based on the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image anyway. Default: `true`. diff --git a/docs/generating_the_url_advanced.md b/docs/generating_the_url_advanced.md index 462b07bb..c00009d7 100644 --- a/docs/generating_the_url_advanced.md +++ b/docs/generating_the_url_advanced.md @@ -486,6 +486,15 @@ scp:%strip_color_profile When set to `1`, `t` or `true`, imgproxy will transform the embedded color profile (ICC) to sRGB and remove it from the image. Otherwise, imgproxy will try to keep it as is. Normally this is controlled by the [IMGPROXY_STRIP_COLOR_PROFILE](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request. +#### Auto Rotate + +``` +auto_rotate:%auto_rotate +ar:%auto_rotate +``` + +When set to `1`, `t` or `true`, imgproxy will automatically rotate images based onon the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image anyway. Normally this is controlled by the [IMGPROXY_AUTO_ROTATE](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request. + #### Filename ``` diff --git a/process.go b/process.go index 4a3b172f..5f60c3af 100644 --- a/process.go +++ b/process.go @@ -34,13 +34,17 @@ func imageTypeGoodForWeb(imgtype imageType) bool { imgtype != imageTypeBMP } -func extractMeta(img *vipsImage) (int, int, int, bool) { +func extractMeta(img *vipsImage, useOrientation bool) (int, int, int, bool) { width := img.Width() height := img.Height() angle := vipsAngleD0 flip := false + if !useOrientation { + return width, height, angle, flip + } + orientation := img.Orientation() if orientation >= 5 && orientation <= 8 { @@ -321,7 +325,7 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces trimmed = true } - srcWidth, srcHeight, angle, flip := extractMeta(img) + srcWidth, srcHeight, angle, flip := extractMeta(img, po.AutoRotate) cropWidth, cropHeight := po.Crop.Width, po.Crop.Height cropGravity := po.Crop.Gravity @@ -352,7 +356,7 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces } // Update scale after scale-on-load - newWidth, newHeight, _, _ := extractMeta(img) + newWidth, newHeight, _, _ := extractMeta(img, po.AutoRotate) if srcWidth > srcHeight { scale = float64(srcWidth) * scale / float64(newWidth) } else { @@ -399,10 +403,8 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces return err } - if angle != vipsAngleD0 { - if err = img.Rotate(angle); err != nil { - return err - } + if err = img.Rotate(angle); err != nil { + return err } if flip { diff --git a/processing_options.go b/processing_options.go index 4e413410..af282d75 100644 --- a/processing_options.go +++ b/processing_options.go @@ -145,6 +145,7 @@ type processingOptions struct { Sharpen float32 StripMetadata bool StripColorProfile bool + AutoRotate bool CacheBuster string @@ -231,6 +232,7 @@ func newProcessingOptions() *processingOptions { Watermark: watermarkOptions{Opacity: 1, Replicate: false, Gravity: gravityOptions{Type: gravityCenter}}, StripMetadata: conf.StripMetadata, StripColorProfile: conf.StripColorProfile, + AutoRotate: conf.AutoRotate, } }) @@ -886,6 +888,16 @@ func applyStripColorProfileOption(po *processingOptions, args []string) error { return nil } +func applyAutoRotateOption(po *processingOptions, args []string) error { + if len(args) > 1 { + return fmt.Errorf("Invalid auto rotate arguments: %v", args) + } + + po.AutoRotate = parseBoolOption(args[0]) + + return nil +} + func applyProcessingOption(po *processingOptions, name string, args []string) error { switch name { case "format", "f", "ext": @@ -934,6 +946,8 @@ func applyProcessingOption(po *processingOptions, name string, args []string) er return applyStripMetadataOption(po, args) case "strip_color_profile", "scp": return applyStripColorProfileOption(po, args) + case "auto_rotate", "ar": + return applyAutoRotateOption(po, args) case "filename", "fn": return applyFilenameOption(po, args) }