mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-09-26 19:46:43 +02:00
Add IMGPROXY_MAX_RESULT_DIMENSION
config and max_result_dimension
processing option
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Add [IMGPROXY_MAX_RESULT_DIMENSION](https://docs.imgproxy.net/latest/configuration/options#IMGPROXY_MAX_RESULT_DIMENSION) config and [max_result_dimension](https://docs.imgproxy.net/latest/usage/processing#max-result-dimension) processing option.
|
||||
- Add `imgproxy.source_image_origin` attribute to New Relic, DataDog, and OpenTelemetry traces.
|
||||
- Add `imgproxy.source_image_url` and `imgproxy.source_image_origin` attributes to `downloading_image` spans in New Relic, DataDog, and OpenTelemetry traces.
|
||||
- Add `imgproxy.processing_options` attribute to `processing_image` spans in New Relic, DataDog, and OpenTelemetry traces.
|
||||
|
@@ -47,6 +47,7 @@ var (
|
||||
MaxRedirects int
|
||||
PngUnlimited bool
|
||||
SvgUnlimited bool
|
||||
MaxResultDimension int
|
||||
AllowSecurityOptions bool
|
||||
|
||||
JpegProgressive bool
|
||||
@@ -252,6 +253,7 @@ func Reset() {
|
||||
MaxRedirects = 10
|
||||
PngUnlimited = false
|
||||
SvgUnlimited = false
|
||||
MaxResultDimension = 0
|
||||
AllowSecurityOptions = false
|
||||
|
||||
JpegProgressive = false
|
||||
@@ -483,6 +485,8 @@ func Configure() error {
|
||||
configurators.Bool(&PngUnlimited, "IMGPROXY_PNG_UNLIMITED")
|
||||
configurators.Bool(&SvgUnlimited, "IMGPROXY_SVG_UNLIMITED")
|
||||
|
||||
configurators.Int(&MaxResultDimension, "IMGPROXY_MAX_RESULT_DIMENSION")
|
||||
|
||||
configurators.Bool(&AllowSecurityOptions, "IMGPROXY_ALLOW_SECURITY_OPTIONS")
|
||||
|
||||
configurators.Bool(&JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
|
||||
|
@@ -966,6 +966,24 @@ func applyMaxAnimationFrameResolutionOption(po *ProcessingOptions, args []string
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyMaxResultDimensionOption(po *ProcessingOptions, args []string) error {
|
||||
if err := security.IsSecurityOptionsAllowed(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
return newOptionArgumentError("Invalid max_result_dimension arguments: %v", args)
|
||||
}
|
||||
|
||||
if x, err := strconv.Atoi(args[0]); err == nil {
|
||||
po.SecurityOptions.MaxResultDimension = x
|
||||
} else {
|
||||
return newOptionArgumentError("Invalid max_result_dimension: %s", args[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyURLOption(po *ProcessingOptions, name string, args []string, usedPresets ...string) error {
|
||||
switch name {
|
||||
case "resize", "rs":
|
||||
@@ -1056,6 +1074,8 @@ func applyURLOption(po *ProcessingOptions, name string, args []string, usedPrese
|
||||
return applyMaxAnimationFramesOption(po, args)
|
||||
case "max_animation_frame_resolution", "mafr":
|
||||
return applyMaxAnimationFrameResolutionOption(po, args)
|
||||
case "max_result_dimension", "mrd":
|
||||
return applyMaxResultDimensionOption(po, args)
|
||||
}
|
||||
|
||||
return newUnknownOptionError("processing", name)
|
||||
|
@@ -204,6 +204,50 @@ func (pctx *pipelineContext) calcSizes(widthToScale, heightToScale int, po *opti
|
||||
}
|
||||
}
|
||||
|
||||
func (pctx *pipelineContext) limitScale(widthToScale, heightToScale int, po *options.ProcessingOptions) {
|
||||
maxresultDim := po.SecurityOptions.MaxResultDimension
|
||||
|
||||
if maxresultDim <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
outWidth := imath.MinNonZero(pctx.scaledWidth, pctx.resultCropWidth)
|
||||
outHeight := imath.MinNonZero(pctx.scaledHeight, pctx.resultCropHeight)
|
||||
|
||||
if po.Extend.Enabled {
|
||||
outWidth = imath.Max(outWidth, pctx.targetWidth)
|
||||
outHeight = imath.Max(outHeight, pctx.targetHeight)
|
||||
} else if po.ExtendAspectRatio.Enabled {
|
||||
outWidth = imath.Max(outWidth, pctx.extendAspectRatioWidth)
|
||||
outHeight = imath.Max(outHeight, pctx.extendAspectRatioHeight)
|
||||
}
|
||||
|
||||
if po.Padding.Enabled {
|
||||
outWidth += imath.ScaleToEven(po.Padding.Left, pctx.dprScale) + imath.ScaleToEven(po.Padding.Right, pctx.dprScale)
|
||||
outHeight += imath.ScaleToEven(po.Padding.Top, pctx.dprScale) + imath.ScaleToEven(po.Padding.Bottom, pctx.dprScale)
|
||||
}
|
||||
|
||||
if maxresultDim > 0 && (outWidth > maxresultDim || outHeight > maxresultDim) {
|
||||
downScale := float64(maxresultDim) / float64(imath.Max(outWidth, outHeight))
|
||||
|
||||
pctx.wscale *= downScale
|
||||
pctx.hscale *= downScale
|
||||
|
||||
// Prevent scaling below 1px
|
||||
if minWScale := 1.0 / float64(widthToScale); pctx.wscale < minWScale {
|
||||
pctx.wscale = minWScale
|
||||
}
|
||||
if minHScale := 1.0 / float64(heightToScale); pctx.hscale < minHScale {
|
||||
pctx.hscale = minHScale
|
||||
}
|
||||
|
||||
pctx.dprScale *= downScale
|
||||
|
||||
// Recalculate the sizes after changing the scales
|
||||
pctx.calcSizes(widthToScale, heightToScale, po)
|
||||
}
|
||||
}
|
||||
|
||||
func prepare(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
|
||||
pctx.imgtype = imagetype.Unknown
|
||||
if imgdata != nil {
|
||||
@@ -233,5 +277,7 @@ func prepare(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptio
|
||||
|
||||
pctx.calcSizes(widthToScale, heightToScale, po)
|
||||
|
||||
pctx.limitScale(widthToScale, heightToScale, po)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -560,6 +560,435 @@ func (s *ProcessingTestSuite) TestResizeToFillDownExtendAR() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProcessingTestSuite) TestResultSizeLimit() {
|
||||
imgdata := s.openFile("test2.jpg")
|
||||
|
||||
po := options.NewProcessingOptions()
|
||||
|
||||
testCases := []struct {
|
||||
limit int
|
||||
width int
|
||||
height int
|
||||
resizingType options.ResizeType
|
||||
enlarge bool
|
||||
extend bool
|
||||
extendAR bool
|
||||
padding options.PaddingOptions
|
||||
rotate int
|
||||
outWidth int
|
||||
outHeight int
|
||||
}{
|
||||
{
|
||||
limit: 1000,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFit,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFit,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 0,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFit,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 0,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFit,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 150,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFit,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFit,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFit,
|
||||
enlarge: true,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 2000,
|
||||
resizingType: options.ResizeFit,
|
||||
extend: true,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 2000,
|
||||
resizingType: options.ResizeFit,
|
||||
extendAR: true,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 100,
|
||||
height: 150,
|
||||
resizingType: options.ResizeFit,
|
||||
rotate: 90,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 0,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFit,
|
||||
rotate: 90,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 200,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFit,
|
||||
padding: options.PaddingOptions{
|
||||
Enabled: true,
|
||||
Top: 100,
|
||||
Right: 200,
|
||||
Bottom: 300,
|
||||
Left: 400,
|
||||
},
|
||||
outWidth: 200,
|
||||
outHeight: 129,
|
||||
},
|
||||
{
|
||||
limit: 1000,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 100,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 50,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 1000,
|
||||
height: 50,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 50,
|
||||
outHeight: 13,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 100,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 50,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 0,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 0,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 150,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFill,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFill,
|
||||
enlarge: true,
|
||||
outWidth: 100,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 2000,
|
||||
resizingType: options.ResizeFill,
|
||||
extend: true,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 2000,
|
||||
resizingType: options.ResizeFill,
|
||||
extendAR: true,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 100,
|
||||
height: 150,
|
||||
resizingType: options.ResizeFill,
|
||||
rotate: 90,
|
||||
outWidth: 67,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 0,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFill,
|
||||
rotate: 90,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 200,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFill,
|
||||
padding: options.PaddingOptions{
|
||||
Enabled: true,
|
||||
Top: 100,
|
||||
Right: 200,
|
||||
Bottom: 300,
|
||||
Left: 400,
|
||||
},
|
||||
outWidth: 200,
|
||||
outHeight: 144,
|
||||
},
|
||||
{
|
||||
limit: 1000,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 100,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 50,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 1000,
|
||||
height: 50,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 50,
|
||||
outHeight: 3,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 100,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 5,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 0,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 0,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 100,
|
||||
outHeight: 50,
|
||||
},
|
||||
{
|
||||
limit: 50,
|
||||
width: 150,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 50,
|
||||
outHeight: 25,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFillDown,
|
||||
outWidth: 100,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFillDown,
|
||||
enlarge: true,
|
||||
outWidth: 100,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 2000,
|
||||
resizingType: options.ResizeFillDown,
|
||||
extend: true,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 2000,
|
||||
resizingType: options.ResizeFillDown,
|
||||
extendAR: true,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 1000,
|
||||
height: 1500,
|
||||
resizingType: options.ResizeFillDown,
|
||||
rotate: 90,
|
||||
outWidth: 67,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 100,
|
||||
width: 0,
|
||||
height: 0,
|
||||
resizingType: options.ResizeFillDown,
|
||||
rotate: 90,
|
||||
outWidth: 50,
|
||||
outHeight: 100,
|
||||
},
|
||||
{
|
||||
limit: 200,
|
||||
width: 100,
|
||||
height: 100,
|
||||
resizingType: options.ResizeFillDown,
|
||||
padding: options.PaddingOptions{
|
||||
Enabled: true,
|
||||
Top: 100,
|
||||
Right: 200,
|
||||
Bottom: 300,
|
||||
Left: 400,
|
||||
},
|
||||
outWidth: 200,
|
||||
outHeight: 144,
|
||||
},
|
||||
{
|
||||
limit: 200,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
resizingType: options.ResizeFillDown,
|
||||
padding: options.PaddingOptions{
|
||||
Enabled: true,
|
||||
Top: 100,
|
||||
Right: 200,
|
||||
Bottom: 300,
|
||||
Left: 400,
|
||||
},
|
||||
outWidth: 200,
|
||||
outHeight: 144,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
name := fmt.Sprintf("%s_%dx%d_limit_%d", tc.resizingType, tc.width, tc.height, tc.limit)
|
||||
if tc.enlarge {
|
||||
name += "_enlarge"
|
||||
}
|
||||
if tc.extend {
|
||||
name += "_extend"
|
||||
}
|
||||
if tc.extendAR {
|
||||
name += "_extendAR"
|
||||
}
|
||||
if tc.rotate != 0 {
|
||||
name += fmt.Sprintf("_rot_%d", tc.rotate)
|
||||
}
|
||||
if tc.padding.Enabled {
|
||||
name += fmt.Sprintf("_padding_%dx%dx%dx%d", tc.padding.Top, tc.padding.Right, tc.padding.Bottom, tc.padding.Left)
|
||||
}
|
||||
|
||||
s.Run(name, func() {
|
||||
po.SecurityOptions.MaxResultDimension = tc.limit
|
||||
po.Width = tc.width
|
||||
po.Height = tc.height
|
||||
po.ResizingType = tc.resizingType
|
||||
po.Enlarge = tc.enlarge
|
||||
po.Extend.Enabled = tc.extend
|
||||
po.ExtendAspectRatio.Enabled = tc.extendAR
|
||||
po.Rotate = tc.rotate
|
||||
po.Padding = tc.padding
|
||||
|
||||
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(outImgdata)
|
||||
|
||||
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessing(t *testing.T) {
|
||||
suite.Run(t, new(ProcessingTestSuite))
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ type Options struct {
|
||||
MaxSrcFileSize int
|
||||
MaxAnimationFrames int
|
||||
MaxAnimationFrameResolution int
|
||||
MaxResultDimension int
|
||||
}
|
||||
|
||||
func DefaultOptions() Options {
|
||||
@@ -17,6 +18,7 @@ func DefaultOptions() Options {
|
||||
MaxSrcFileSize: config.MaxSrcFileSize,
|
||||
MaxAnimationFrames: config.MaxAnimationFrames,
|
||||
MaxAnimationFrameResolution: config.MaxAnimationFrameResolution,
|
||||
MaxResultDimension: config.MaxResultDimension,
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user