From 134480146bf9ec317d97de0e82a5f4293712ad9e Mon Sep 17 00:00:00 2001 From: Eric Case Date: Thu, 21 May 2026 21:50:49 -0700 Subject: [PATCH 1/3] Fix fmtFloat truncation when rounded value is whole --- fsthttp/imageopto/imageopto.go | 5 +---- fsthttp/imageopto/imageopto_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fsthttp/imageopto/imageopto.go b/fsthttp/imageopto/imageopto.go index bc825d7..9bbe7e7 100644 --- a/fsthttp/imageopto/imageopto.go +++ b/fsthttp/imageopto/imageopto.go @@ -57,10 +57,7 @@ func (a Auto) String() string { return string(a) } func fmtFloat(f float64) string { nearest3 := math.Round(1000.0*f) / 1000.0 - if _, fract := math.Modf(nearest3); fract > 0.0 { - return fmt.Sprintf("%v", nearest3) - } - return fmt.Sprintf("%v", int32(f)) + return strconv.FormatFloat(nearest3, 'g', -1, 64) } // PixelsOrPercentage specifies a position along an exist. diff --git a/fsthttp/imageopto/imageopto_test.go b/fsthttp/imageopto/imageopto_test.go index 9ef37d2..5f450af 100644 --- a/fsthttp/imageopto/imageopto_test.go +++ b/fsthttp/imageopto/imageopto_test.go @@ -199,6 +199,16 @@ func TestOpts(t *testing.T) { "region=us_east&precrop=1:1,offset-x30,offset-y15,safe", }, + + // fmtFloat: a value that rounds to a whole number must format as that + // whole number. Sharpen.Radius=1.9999 rounds to 2.0 -> "r2". + { + &Options{ + Region: RegionUsEast, + Sharpen: &Sharpen{Amount: 5, Radius: 1.9999, Threshold: 1}, + }, + "region=us_east&sharpen=a5,r2,t1", + }, } for i, tt := range tests { From 1659098df5882dd01dc339b9cb8fb9720e8fe3ab Mon Sep 17 00:00:00 2001 From: Eric Case Date: Thu, 21 May 2026 21:51:42 -0700 Subject: [PATCH 2/3] Reduce fmt usage in String() methods Addresses #199. Also fixes some typos --- fsthttp/imageopto/imageopto.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/fsthttp/imageopto/imageopto.go b/fsthttp/imageopto/imageopto.go index 9bbe7e7..ab488ff 100644 --- a/fsthttp/imageopto/imageopto.go +++ b/fsthttp/imageopto/imageopto.go @@ -60,7 +60,7 @@ func fmtFloat(f float64) string { return strconv.FormatFloat(nearest3, 'g', -1, 64) } -// PixelsOrPercentage specifies a position along an exist. +// PixelsOrPercentage specifies a length as either pixels or a percentage. // // Valid values are either an integer number of pixels, or a percentage followed by `p`. type PixelsOrPercentage string @@ -155,9 +155,9 @@ func (area *Area) String() string { case areaStateNone: return "" case areaStateAspectRatio: - return fmt.Sprintf("%v:%v", area.aspect.w, area.aspect.h) + return strconv.FormatUint(uint64(area.aspect.w), 10) + ":" + strconv.FormatUint(uint64(area.aspect.h), 10) case areaStateWidthHeight: - return fmt.Sprintf("%v,%v", area.widthHeight.w, area.widthHeight.h) + return string(area.widthHeight.w) + "," + string(area.widthHeight.h) } return "error" } @@ -253,7 +253,7 @@ type Sides struct { } func (s *Sides) String() string { - return fmt.Sprintf("%v,%v,%v,%v", s.Top, s.Right, s.Bottom, s.Left) + return string(s.Top) + "," + string(s.Right) + "," + string(s.Bottom) + "," + string(s.Left) } func (s *Sides) validate() error { @@ -286,7 +286,7 @@ func (s *Sides) validate() error { type OptimizeLevel string const ( - // OptimizeLevelLow means the output mage quality will be similar to the input image quality. + // OptimizeLevelLow means the output image quality will be similar to the input image quality. OptimizeLevelLow OptimizeLevel = "low" // OptimizeLevelMedium means more optimization is allowed, while attempting to preserve the visual @@ -326,8 +326,10 @@ type HexColor struct { } func (h *HexColor) String() string { - return fmt.Sprintf("%v,%v,%v,%v", h.R, h.G, h.B, h.A) - + return strconv.Itoa(int(h.R)) + "," + + strconv.Itoa(int(h.G)) + "," + + strconv.Itoa(int(h.B)) + "," + + strconv.FormatFloat(float64(h.A), 'g', -1, 32) } func (h *HexColor) validate() error { @@ -438,9 +440,9 @@ func (b *BlurMode) String() string { case blurModeStateNone: return "" case blurModeStatePercentage: - return fmt.Sprintf("%vp", b.percentage) + return strconv.FormatFloat(b.percentage, 'g', -1, 64) + "p" case blurModeStatePixels: - return fmt.Sprintf("%v", b.pixels) + return strconv.FormatFloat(b.pixels, 'g', -1, 64) } return "error" @@ -605,7 +607,9 @@ type Sharpen struct { } func (s *Sharpen) String() string { - return fmt.Sprintf("a%v,r%v,t%v", s.Amount, fmtFloat(float64(s.Radius)), s.Threshold) + return "a" + strconv.Itoa(int(s.Amount)) + + ",r" + fmtFloat(float64(s.Radius)) + + ",t" + strconv.Itoa(int(s.Threshold)) } func (s *Sharpen) validate() error { @@ -623,7 +627,7 @@ func (s *Sharpen) validate() error { type EnableOpt string const ( - /// EnableOptUpsace allow images to be resized such that the output image's dimensions are + /// EnableOptUpscale allows images to be resized such that the output image's dimensions are /// larger than the source image. EnableOptUpscale EnableOpt = "upscale" ) @@ -886,7 +890,7 @@ func (o *Options) QueryString() (string, error) { } if o.Dpr != 0 { - args = append(args, "dpr="+fmt.Sprintf("%v", o.Dpr)) + args = append(args, "dpr="+strconv.FormatFloat(float64(o.Dpr), 'g', -1, 32)) } if o.Enable.isSet() { From 574d781a4672317dfbe0de5d7862ae1b66a358d1 Mon Sep 17 00:00:00 2001 From: Eric Case Date: Fri, 22 May 2026 15:25:12 -0700 Subject: [PATCH 3/3] Address review feedback --- fsthttp/imageopto/imageopto.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fsthttp/imageopto/imageopto.go b/fsthttp/imageopto/imageopto.go index ab488ff..4fad131 100644 --- a/fsthttp/imageopto/imageopto.go +++ b/fsthttp/imageopto/imageopto.go @@ -157,7 +157,7 @@ func (area *Area) String() string { case areaStateAspectRatio: return strconv.FormatUint(uint64(area.aspect.w), 10) + ":" + strconv.FormatUint(uint64(area.aspect.h), 10) case areaStateWidthHeight: - return string(area.widthHeight.w) + "," + string(area.widthHeight.h) + return area.widthHeight.w.String() + "," + area.widthHeight.h.String() } return "error" } @@ -253,7 +253,7 @@ type Sides struct { } func (s *Sides) String() string { - return string(s.Top) + "," + string(s.Right) + "," + string(s.Bottom) + "," + string(s.Left) + return s.Top.String() + "," + s.Right.String() + "," + s.Bottom.String() + "," + s.Left.String() } func (s *Sides) validate() error {