Skip to content

Commit b9cc9df

Browse files
committed
add support for specifying output image format
For now, the options are "jpeg" and "png". Gif is a little harder to support because of the way we use the image/gif package to handle animated gifs. I have also have trouble imagining someone wanting to use gif over png. But if the need really exists, we can address it when it comes up. Fixes #89
1 parent 2937bf8 commit b9cc9df

4 files changed

Lines changed: 37 additions & 8 deletions

File tree

data.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const (
2828
optFit = "fit"
2929
optFlipVertical = "fv"
3030
optFlipHorizontal = "fh"
31+
optFormatJPEG = "jpeg"
32+
optFormatPNG = "png"
3133
optRotatePrefix = "r"
3234
optQualityPrefix = "q"
3335
optSignaturePrefix = "s"
@@ -71,6 +73,9 @@ type Options struct {
7173
// Allow image to scale beyond its original dimensions. This value
7274
// will always be overwritten by the value of Proxy.ScaleUp.
7375
ScaleUp bool
76+
77+
// Desired image format. Valid values are "jpeg", "png".
78+
Format string
7479
}
7580

7681
func (o Options) String() string {
@@ -97,14 +102,18 @@ func (o Options) String() string {
97102
if o.ScaleUp {
98103
fmt.Fprintf(buf, ",%s", optScaleUp)
99104
}
105+
if o.Format != "" {
106+
fmt.Fprintf(buf, ",%s", o.Format)
107+
}
100108
return buf.String()
101109
}
102110

103111
// transform returns whether o includes transformation options. Some fields
104112
// are not transform related at all (like Signature), and others only apply in
105-
// the presence of other fields (like Fit and Quality).
113+
// the presence of other fields (like Fit and Quality). A non-empty Format
114+
// value is assumed to involve a transformation.
106115
func (o Options) transform() bool {
107-
return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical
116+
return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Format != ""
108117
}
109118

110119
// ParseOptions parses str as a list of comma separated transformation options.
@@ -153,6 +162,11 @@ func (o Options) transform() bool {
153162
// The "q{qualityPercentage}" option can be used to specify the quality of the
154163
// output file (JPEG only). If not specified, the default value of "95" is used.
155164
//
165+
// Format
166+
//
167+
// The "jpeg" and "png" options can be used to specify the desired image format
168+
// of the proxied image.
169+
//
156170
// Signature
157171
//
158172
// The "s{signature}" option specifies an optional base64 encoded HMAC used to
@@ -174,6 +188,7 @@ func (o Options) transform() bool {
174188
// 100,r90 - 100 pixels square, rotated 90 degrees
175189
// 100,fv,fh - 100 pixels square, flipped horizontal and vertical
176190
// 200x,q80 - 200 pixels wide, proportional height, 80% quality
191+
// 200x,png - 200 pixels wide, converted to PNG format
177192
func ParseOptions(str string) Options {
178193
var options Options
179194

@@ -189,6 +204,8 @@ func ParseOptions(str string) Options {
189204
options.FlipHorizontal = true
190205
case opt == optScaleUp: // this option is intentionally not documented above
191206
options.ScaleUp = true
207+
case opt == optFormatJPEG, opt == optFormatPNG:
208+
options.Format = opt
192209
case strings.HasPrefix(opt, optRotatePrefix):
193210
value := strings.TrimPrefix(opt, optRotatePrefix)
194211
options.Rotate, _ = strconv.Atoi(value)

data_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ func TestOptions_String(t *testing.T) {
3131
"0x0",
3232
},
3333
{
34-
Options{1, 2, true, 90, true, true, 80, "", false},
34+
Options{1, 2, true, 90, true, true, 80, "", false, ""},
3535
"1x2,fit,r90,fv,fh,q80",
3636
},
3737
{
38-
Options{0.15, 1.3, false, 45, false, false, 95, "c0ffee", false},
39-
"0.15x1.3,r45,q95,sc0ffee",
38+
Options{0.15, 1.3, false, 45, false, false, 95, "c0ffee", false, "png"},
39+
"0.15x1.3,r45,q95,sc0ffee,png",
4040
},
4141
}
4242

@@ -72,20 +72,22 @@ func TestParseOptions(t *testing.T) {
7272
{"r90", Options{Rotate: 90}},
7373
{"fv", Options{FlipVertical: true}},
7474
{"fh", Options{FlipHorizontal: true}},
75+
{"jpeg", Options{Format: "jpeg"}},
7576

7677
// duplicate flags (last one wins)
7778
{"1x2,3x4", Options{Width: 3, Height: 4}},
7879
{"1x2,3", Options{Width: 3, Height: 3}},
7980
{"1x2,0x3", Options{Width: 0, Height: 3}},
8081
{"1x,x2", Options{Width: 1, Height: 2}},
8182
{"r90,r270", Options{Rotate: 270}},
83+
{"jpeg,png", Options{Format: "png"}},
8284

8385
// mix of valid and invalid flags
8486
{"FOO,1,BAR,r90,BAZ", Options{Width: 1, Height: 1, Rotate: 90}},
8587

8688
// all flags, in different orders
87-
{"q70,1x2,fit,r90,fv,fh,sc0ffee", Options{1, 2, true, 90, true, true, 70, "c0ffee", false}},
88-
{"r90,fh,sc0ffee,q90,1x2,fv,fit", Options{1, 2, true, 90, true, true, 90, "c0ffee", false}},
89+
{"q70,1x2,fit,r90,fv,fh,sc0ffee,png", Options{1, 2, true, 90, true, true, 70, "c0ffee", false, "png"}},
90+
{"r90,fh,sc0ffee,png,q90,1x2,fv,fit", Options{1, 2, true, 90, true, true, 90, "c0ffee", false, "png"}},
8991
}
9092

9193
for _, tt := range tests {

imageproxy.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,10 @@ func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, er
306306
// replay response with transformed image and updated content length
307307
buf := new(bytes.Buffer)
308308
fmt.Fprintf(buf, "%s %s\n", resp.Proto, resp.Status)
309-
resp.Header.WriteSubset(buf, map[string]bool{"Content-Length": true})
309+
resp.Header.WriteSubset(buf, map[string]bool{
310+
"Content-Length": true,
311+
"Content-Type": opt.Format != "",
312+
})
310313
fmt.Fprintf(buf, "Content-Length: %d\n\n", len(img))
311314
buf.Write(img)
312315

transform.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package imageproxy
1616

1717
import (
1818
"bytes"
19+
"fmt"
1920
"image"
2021
_ "image/gif" // register gif format
2122
"image/jpeg"
@@ -46,6 +47,10 @@ func Transform(img []byte, opt Options) ([]byte, error) {
4647
return nil, err
4748
}
4849

50+
if opt.Format != "" {
51+
format = opt.Format
52+
}
53+
4954
// transform and encode image
5055
buf := new(bytes.Buffer)
5156
switch format {
@@ -74,6 +79,8 @@ func Transform(img []byte, opt Options) ([]byte, error) {
7479
if err != nil {
7580
return nil, err
7681
}
82+
default:
83+
return nil, fmt.Errorf("unsupported format: %v", format)
7784
}
7885

7986
return buf.Bytes(), nil

0 commit comments

Comments
 (0)