accept "fit" option

this also relaxes option parsing to never return an error.  In cases
where options are not able to be parsed, they are silently ignored.
This commit is contained in:
Will Norris 2013-12-06 11:01:34 -08:00
parent 805aa606ca
commit 2794d47390
4 changed files with 47 additions and 23 deletions

View file

@ -2,7 +2,6 @@
package data package data
import ( import (
"errors"
"fmt" "fmt"
"net/url" "net/url"
"strconv" "strconv"
@ -15,18 +14,24 @@ import (
type Options struct { type Options struct {
Width int // requested width, in pixels Width int // requested width, in pixels
Height int // requested height, in pixels Height int // requested height, in pixels
// If true, resize the image to fit in the specified dimensions. Image
// will not be cropped, and aspect ratio will be maintained.
Fit bool
} }
func (o Options) String() string { func (o Options) String() string {
return fmt.Sprintf("%dx%d", o.Width, o.Height) return fmt.Sprintf("%dx%d", o.Width, o.Height)
} }
func ParseOptions(str string) (*Options, error) { func ParseOptions(str string) *Options {
t := new(Options) o := new(Options)
var err error
var h, w string var h, w string
size := strings.SplitN(str, "x", 2) parts := strings.Split(str, ",")
// parse size
size := strings.SplitN(parts[0], "x", 2)
w = size[0] w = size[0]
if len(size) > 1 { if len(size) > 1 {
h = size[1] h = size[1]
@ -35,19 +40,19 @@ func ParseOptions(str string) (*Options, error) {
} }
if w != "" { if w != "" {
t.Width, err = strconv.Atoi(w) o.Width, _ = strconv.Atoi(w)
if err != nil {
return nil, errors.New("width must be an int")
}
} }
if h != "" { if h != "" {
t.Height, err = strconv.Atoi(h) o.Height, _ = strconv.Atoi(h)
if err != nil { }
return nil, errors.New("height must be an int")
for _, part := range parts[1:] {
if part == "fit" {
o.Fit = true
} }
} }
return t, nil return o
} }
type Request struct { type Request struct {

View file

@ -46,10 +46,7 @@ func NewRequest(r *http.Request) (*data.Request, error) {
return nil, URLError{fmt.Sprintf("unable to parse remote URL: %v", err), r.URL} return nil, URLError{fmt.Sprintf("unable to parse remote URL: %v", err), r.URL}
} }
req.Options, err = data.ParseOptions(parts[0]) req.Options = data.ParseOptions(parts[0])
if err != nil {
return nil, URLError{err.Error(), r.URL}
}
} }
if !req.URL.IsAbs() { if !req.URL.IsAbs() {

View file

@ -30,11 +30,15 @@ func TestNewRequest(t *testing.T) {
{ {
"http://localhost//ftp://example.com/foo", "", nil, true, "http://localhost//ftp://example.com/foo", "", nil, true,
}, },
// invalid options. These won't return errors, but will not fully parse the options
{ {
"http://localhost/s/http://example.com/", "", nil, true, "http://localhost/s/http://example.com/",
"http://example.com/", emptyOptions, false,
}, },
{ {
"http://localhost/1xs/http://example.com/", "", nil, true, "http://localhost/1xs/http://example.com/",
"http://example.com/", &data.Options{Width: 1}, false,
}, },
// valid URLs // valid URLs
@ -66,15 +70,23 @@ func TestNewRequest(t *testing.T) {
}, },
{ {
"http://localhost/1x/http://example.com/", "http://localhost/1x/http://example.com/",
"http://example.com/", &data.Options{1, 0}, false, "http://example.com/", &data.Options{1, 0, false}, false,
}, },
{ {
"http://localhost/x1/http://example.com/", "http://localhost/x1/http://example.com/",
"http://example.com/", &data.Options{0, 1}, false, "http://example.com/", &data.Options{0, 1, false}, false,
}, },
{ {
"http://localhost/1x2/http://example.com/", "http://localhost/1x2/http://example.com/",
"http://example.com/", &data.Options{1, 2}, false, "http://example.com/", &data.Options{1, 2, false}, false,
},
{
"http://localhost/,fit/http://example.com/",
"http://example.com/", &data.Options{0, 0, true}, false,
},
{
"http://localhost/1x2,fit/http://example.com/",
"http://example.com/", &data.Options{1, 2, true}, false,
}, },
} }

View file

@ -18,6 +18,14 @@ var emptyOptions = new(data.Options)
// Transform the provided image. // Transform the provided image.
func Transform(img data.Image, opt *data.Options) (*data.Image, error) { func Transform(img data.Image, opt *data.Options) (*data.Image, error) {
if opt == nil || reflect.DeepEqual(opt, emptyOptions) { if opt == nil || reflect.DeepEqual(opt, emptyOptions) {
// bail if no transformation was requested
return &img, nil
}
if opt.Width == 0 && opt.Height == 0 {
// TODO(willnorris): Currently, only resize related options are
// supported, so bail if no sizes are specified. Remove this
// check if we ever support non-resizing transformations.
return &img, nil return &img, nil
} }
@ -28,8 +36,10 @@ func Transform(img data.Image, opt *data.Options) (*data.Image, error) {
} }
// resize // resize
if opt.Width != 0 || opt.Height != 0 { if opt.Fit {
m = imaging.Fit(m, opt.Width, opt.Height, imaging.Lanczos) m = imaging.Fit(m, opt.Width, opt.Height, imaging.Lanczos)
} else {
m = imaging.Resize(m, opt.Width, opt.Height, imaging.Lanczos)
} }
// encode image // encode image