diff --git a/Dockerfile b/Dockerfile index 4a00e45..144c6fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM dpokidov/imagemagick:7.1.1-17-bullseye AS build +FROM dpokidov/imagemagick:7.1.1-22-bullseye AS build RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ g++ \ @@ -16,41 +16,44 @@ RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-reco #Installing golang ENV PATH /usr/local/go/bin:$PATH -ENV GOLANG_VERSION 1.20.5 +ENV GOLANG_VERSION 1.20.12 RUN set -eux; \ arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \ url=; \ case "$arch" in \ 'amd64') \ - url='https://dl.google.com/go/go1.20.5.linux-amd64.tar.gz'; \ - sha256='d7ec48cde0d3d2be2c69203bc3e0a44de8660b9c09a6e85c4732a3f7dc442612'; \ + url='https://dl.google.com/go/go1.20.12.linux-amd64.tar.gz'; \ + sha256='9c5d48c54dd8b0a3b2ef91b0f92a1190aa01f11d26e98033efa64c46a30bba7b'; \ ;; \ 'armel') \ export GOARCH='arm' GOARM='5' GOOS='linux'; \ ;; \ 'armhf') \ - url='https://dl.google.com/go/go1.20.5.linux-armv6l.tar.gz'; \ - sha256='79d8210efd4390569912274a98dffc16eb85993cccdeef4d704e9b0dfd50743a'; \ + url='https://dl.google.com/go/go1.20.12.linux-armv6l.tar.gz'; \ + sha256='bf4687cbbf0c44a82311d52e2dcccb263f3d9c3b512007e1fae569e03dc0a189'; \ ;; \ 'arm64') \ - url='https://dl.google.com/go/go1.20.5.linux-arm64.tar.gz'; \ - sha256='aa2fab0a7da20213ff975fa7876a66d47b48351558d98851b87d1cfef4360d09'; \ + url='https://dl.google.com/go/go1.20.12.linux-arm64.tar.gz'; \ + sha256='8afe8e3fb6972eaa2179ef0a71678c67f26509fab4f0f67c4b00f4cdfa92dc87'; \ ;; \ 'i386') \ - url='https://dl.google.com/go/go1.20.5.linux-386.tar.gz'; \ - sha256='d394ac8fecf66812c78ffba7fb9a265bb1b9917564c7fd77f0edb0df6d5777a1'; \ + url='https://dl.google.com/go/go1.20.12.linux-386.tar.gz'; \ + sha256='77db17c6350448b0c3afa5c4248426d8a445b5e8ac2411fff57463b47f8d7f80'; \ ;; \ 'mips64el') \ export GOARCH='mips64le' GOOS='linux'; \ ;; \ 'ppc64el') \ - url='https://dl.google.com/go/go1.20.5.linux-ppc64le.tar.gz'; \ - sha256='049b8ab07d34077b90c0642138e10207f6db14bdd1743ea994a21e228f8ca53d'; \ + url='https://dl.google.com/go/go1.20.12.linux-ppc64le.tar.gz'; \ + sha256='2ae0ec3736216dfbd7b01ff679842dc1bed365e53a024d522645bcffd01c7328'; \ + ;; \ + 'riscv64') \ + export GOARCH='riscv64' GOOS='linux'; \ ;; \ 's390x') \ - url='https://dl.google.com/go/go1.20.5.linux-s390x.tar.gz'; \ - sha256='bac14667f1217ccce1d2ef4e204687fe6191e6dc19a8870cfb81a41f78b04e48'; \ + url='https://dl.google.com/go/go1.20.12.linux-s390x.tar.gz'; \ + sha256='ee48b23e1978a866cb60a8e8ddf0bd61cbbaf86bcfcdbf4f9509f34e9159ce45'; \ ;; \ *) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \ esac; \ @@ -58,8 +61,8 @@ RUN set -eux; \ if [ -z "$url" ]; then \ # https://github.com/golang/go/issues/38536#issuecomment-616897960 build=1; \ - url='https://dl.google.com/go/go1.20.5.src.tar.gz'; \ - sha256='9a15c133ba2cfafe79652f4815b62e7cfc267f68df1b9454c6ab2a3ca8b96a88'; \ + url='https://dl.google.com/go/go1.20.12.src.tar.gz'; \ + sha256='c5bf934751d31c315c1d0bb5fb02296545fa6d08923566f7a5afec81f2ed27d6'; \ echo >&2; \ echo >&2 "warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source"; \ echo >&2; \ @@ -134,7 +137,7 @@ WORKDIR /go/src/github.com/Pixboost/transformimgs/cmd RUN go build -o /transformimgs -FROM dpokidov/imagemagick:7.1.1-17-bullseye +FROM dpokidov/imagemagick:7.1.1-22-bullseye ENV IM_HOME /usr/local/bin diff --git a/Dockerfile.dev b/Dockerfile.dev index 6c1ed10..a0d44e7 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM dpokidov/imagemagick:7.1.1-17-bullseye +FROM dpokidov/imagemagick:7.1.1-22-bullseye RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ g++ \ @@ -16,41 +16,44 @@ RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-reco #Installing golang ENV PATH /usr/local/go/bin:$PATH -ENV GOLANG_VERSION 1.20.5 +ENV GOLANG_VERSION 1.20.12 RUN set -eux; \ arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \ url=; \ case "$arch" in \ 'amd64') \ - url='https://dl.google.com/go/go1.20.5.linux-amd64.tar.gz'; \ - sha256='d7ec48cde0d3d2be2c69203bc3e0a44de8660b9c09a6e85c4732a3f7dc442612'; \ + url='https://dl.google.com/go/go1.20.12.linux-amd64.tar.gz'; \ + sha256='9c5d48c54dd8b0a3b2ef91b0f92a1190aa01f11d26e98033efa64c46a30bba7b'; \ ;; \ 'armel') \ export GOARCH='arm' GOARM='5' GOOS='linux'; \ ;; \ 'armhf') \ - url='https://dl.google.com/go/go1.20.5.linux-armv6l.tar.gz'; \ - sha256='79d8210efd4390569912274a98dffc16eb85993cccdeef4d704e9b0dfd50743a'; \ + url='https://dl.google.com/go/go1.20.12.linux-armv6l.tar.gz'; \ + sha256='bf4687cbbf0c44a82311d52e2dcccb263f3d9c3b512007e1fae569e03dc0a189'; \ ;; \ 'arm64') \ - url='https://dl.google.com/go/go1.20.5.linux-arm64.tar.gz'; \ - sha256='aa2fab0a7da20213ff975fa7876a66d47b48351558d98851b87d1cfef4360d09'; \ + url='https://dl.google.com/go/go1.20.12.linux-arm64.tar.gz'; \ + sha256='8afe8e3fb6972eaa2179ef0a71678c67f26509fab4f0f67c4b00f4cdfa92dc87'; \ ;; \ 'i386') \ - url='https://dl.google.com/go/go1.20.5.linux-386.tar.gz'; \ - sha256='d394ac8fecf66812c78ffba7fb9a265bb1b9917564c7fd77f0edb0df6d5777a1'; \ + url='https://dl.google.com/go/go1.20.12.linux-386.tar.gz'; \ + sha256='77db17c6350448b0c3afa5c4248426d8a445b5e8ac2411fff57463b47f8d7f80'; \ ;; \ 'mips64el') \ export GOARCH='mips64le' GOOS='linux'; \ ;; \ 'ppc64el') \ - url='https://dl.google.com/go/go1.20.5.linux-ppc64le.tar.gz'; \ - sha256='049b8ab07d34077b90c0642138e10207f6db14bdd1743ea994a21e228f8ca53d'; \ + url='https://dl.google.com/go/go1.20.12.linux-ppc64le.tar.gz'; \ + sha256='2ae0ec3736216dfbd7b01ff679842dc1bed365e53a024d522645bcffd01c7328'; \ + ;; \ + 'riscv64') \ + export GOARCH='riscv64' GOOS='linux'; \ ;; \ 's390x') \ - url='https://dl.google.com/go/go1.20.5.linux-s390x.tar.gz'; \ - sha256='bac14667f1217ccce1d2ef4e204687fe6191e6dc19a8870cfb81a41f78b04e48'; \ + url='https://dl.google.com/go/go1.20.12.linux-s390x.tar.gz'; \ + sha256='ee48b23e1978a866cb60a8e8ddf0bd61cbbaf86bcfcdbf4f9509f34e9159ce45'; \ ;; \ *) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \ esac; \ @@ -58,8 +61,8 @@ RUN set -eux; \ if [ -z "$url" ]; then \ # https://github.com/golang/go/issues/38536#issuecomment-616897960 build=1; \ - url='https://dl.google.com/go/go1.20.5.src.tar.gz'; \ - sha256='9a15c133ba2cfafe79652f4815b62e7cfc267f68df1b9454c6ab2a3ca8b96a88'; \ + url='https://dl.google.com/go/go1.20.12.src.tar.gz'; \ + sha256='c5bf934751d31c315c1d0bb5fb02296545fa6d08923566f7a5afec81f2ed27d6'; \ echo >&2; \ echo >&2 "warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source"; \ echo >&2; \ diff --git a/README.md b/README.md index df3d8ff..5c95e0c 100644 --- a/README.md +++ b/README.md @@ -166,13 +166,8 @@ $ jmeter -n -t perf-test-jxl.jmx -l ./results-jxl.jmx -e -o ./results-jxl ## Opened tickets for images related features * [Safari to support Save-Data](https://bugs.webkit.org/show_bug.cgi?id=199101) -* [Safari to support AVIF](https://bugs.webkit.org/show_bug.cgi?id=207750) -* [Firefox to support JPEG XL](https://bugzilla.mozilla.org/show_bug.cgi?id=1539075) -* [Chrome to support JPEG XL](https://bugs.chromium.org/p/chromium/issues/detail?id=1178058) -* [Safari to support JPEG XL](https://bugs.webkit.org/show_bug.cgi?id=208235) -* Safari to support native lazy loading - * [Implementation](https://bugs.webkit.org/show_bug.cgi?id=196698) - * [Enabled by default](https://bugs.webkit.org/show_bug.cgi?id=208094) +* [Auto sizes for lazy loaded img in Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=1816615) +* [Auto sizes for lazy loaded img in Safari](https://bugs.webkit.org/show_bug.cgi?id=253143) ## Contribute diff --git a/img/processor/imagemagick.go b/img/processor/imagemagick.go index 967ec43..a632161 100644 --- a/img/processor/imagemagick.go +++ b/img/processor/imagemagick.go @@ -148,7 +148,7 @@ func (p *ImageMagick) Resize(config *img.TransformationConfig) (*img.Image, erro args := make([]string, 0) args = append(args, "-") //Input - args = append(args, getBeforeTransformConvertFormatOptions(source, mimeType)...) + args = append(args, getBeforeTransformConvertFormatOptions(config, source, mimeType)...) args = append(args, beforeResizeConvertOpts...) args = append(args, "-resize", targetSize) args = append(args, getQualityOptions(source, config, mimeType)...) @@ -199,7 +199,7 @@ func (p *ImageMagick) FitToSize(config *img.TransformationConfig) (*img.Image, e args := make([]string, 0) args = append(args, "-") //Input - args = append(args, getBeforeTransformConvertFormatOptions(source, mimeType)...) + args = append(args, getBeforeTransformConvertFormatOptions(config, source, mimeType)...) args = append(args, beforeResizeConvertOpts...) args = append(args, "-resize", targetSize+"^") @@ -241,7 +241,7 @@ func (p *ImageMagick) Optimise(config *img.TransformationConfig) (*img.Image, er args := make([]string, 0) args = append(args, "-") //Input - args = append(args, getBeforeTransformConvertFormatOptions(source, mimeType)...) + args = append(args, getBeforeTransformConvertFormatOptions(config, source, mimeType)...) args = append(args, beforeResizeConvertOpts...) args = append(args, getQualityOptions(source, config, mimeType)...) args = append(args, p.AdditionalArgs...) @@ -495,12 +495,15 @@ func getConvertFormatOptions(source *img.Info) []string { return opts } -func getBeforeTransformConvertFormatOptions(source *img.Info, outputMimeType string) []string { +func getBeforeTransformConvertFormatOptions(config *img.TransformationConfig, source *img.Info, outputMimeType string) []string { var opts []string if outputMimeType == "image/webp" && source.Format == "GIF" { opts = append(opts, "-coalesce") } + if config.TrimBorder { + opts = append(opts, "-trim") + } return opts } diff --git a/img/processor/imagemagick_test.go b/img/processor/imagemagick_test.go index 2ee8e7f..1657b6a 100644 --- a/img/processor/imagemagick_test.go +++ b/img/processor/imagemagick_test.go @@ -1,12 +1,16 @@ package processor_test import ( + "bytes" "fmt" "github.com/Pixboost/transformimgs/v8/img" "github.com/Pixboost/transformimgs/v8/img/processor" "io/ioutil" "os" + "os/exec" + "path/filepath" "reflect" + "strconv" "testing" ) @@ -548,6 +552,107 @@ func TestImageMagick_IsIllustration(t *testing.T) { } } +var trimBorderTestFiles = []string{"logo-1.png", "logo-2.png", "no-border.jpg"} + +func TestImageMagick_TrimBorder(t *testing.T) { + var overrideExpected = false + + for _, tt := range trimBorderTestFiles { + f := fmt.Sprintf("%s/%s", "./test_files/trim-border", tt) + + orig, err := ioutil.ReadFile(f) + if err != nil { + t.Errorf("can't read file %s: %+v", f, err) + } + + resultImage, err := proc.Optimise(&img.TransformationConfig{ + Src: &img.Image{ + Id: "img", + Data: orig, + }, + TrimBorder: true, + }) + + if err != nil { + t.Errorf("couldn't optimise image %s", tt) + } + + expectedFile := fmt.Sprintf("./test_files/trim-border/expected/optimise/%s", tt) + compareImage(resultImage, expectedFile, t, overrideExpected) + + resultImage, err = proc.Resize(&img.TransformationConfig{ + Src: &img.Image{ + Id: "img", + Data: orig, + }, + TrimBorder: true, + Config: &img.ResizeConfig{Size: "300"}, + }) + + if err != nil { + t.Errorf("couldn't resize image %s", tt) + } + + expectedFile = fmt.Sprintf("./test_files/trim-border/expected/resize/%s", tt) + compareImage(resultImage, expectedFile, t, overrideExpected) + + resultImage, err = proc.FitToSize(&img.TransformationConfig{ + Src: &img.Image{ + Id: "img", + Data: orig, + }, + TrimBorder: true, + Config: &img.ResizeConfig{Size: "200x80"}, + }) + + if err != nil { + t.Errorf("couldn't fit image %s", tt) + } + + expectedFile = fmt.Sprintf("./test_files/trim-border/expected/fit/%s", tt) + compareImage(resultImage, expectedFile, t, overrideExpected) + } +} + +func compareImage(img *img.Image, expectedFile string, t *testing.T, overrideExpected bool) { + if overrideExpected { + ioutil.WriteFile(expectedFile, img.Data, 0777) + } else { + ext := filepath.Ext(expectedFile) + actualFile, err := os.CreateTemp("", fmt.Sprintf("image*%s", ext)) + if err != nil { + t.Errorf("could not create temp file %s", err) + } + _, err = actualFile.Write(img.Data) + if err != nil { + t.Errorf("could not write to temp file %s", err) + } + _ = actualFile.Close() + + var out, cmderr bytes.Buffer + cmd := exec.Command(os.ExpandEnv("${IM_HOME}/magick")) + cmd.Args = append(cmd.Args, "compare", "-metric", "AE", actualFile.Name(), expectedFile, "null:") + cmd.Stdout = &out + cmd.Stderr = &cmderr + + fmt.Println(cmd.Args) + + err = cmd.Run() + errStr := cmderr.String() + outStr := out.String() + if err != nil { + t.Errorf("error executing compare command: %s, %s", err.Error(), errStr) + } + pixelsDiffCnt, err := strconv.Atoi(errStr) + if err != nil { + t.Errorf("could not parse output of compare [%s]", outStr) + } + if pixelsDiffCnt > 0 { + t.Errorf("expected 0 different pixels but found %d when comparing %s", pixelsDiffCnt, expectedFile) + } + } +} + func testImages(t *testing.T, fn transform, files []*testTransformation) { results := make([]*result, 0) for _, tt := range files { diff --git a/img/processor/test_files/trim-border/expected/fit/logo-1.png b/img/processor/test_files/trim-border/expected/fit/logo-1.png new file mode 100755 index 0000000..eb7081c Binary files /dev/null and b/img/processor/test_files/trim-border/expected/fit/logo-1.png differ diff --git a/img/processor/test_files/trim-border/expected/fit/logo-2.png b/img/processor/test_files/trim-border/expected/fit/logo-2.png new file mode 100755 index 0000000..32421a6 Binary files /dev/null and b/img/processor/test_files/trim-border/expected/fit/logo-2.png differ diff --git a/img/processor/test_files/trim-border/expected/fit/no-border.jpg b/img/processor/test_files/trim-border/expected/fit/no-border.jpg new file mode 100755 index 0000000..56b6b38 Binary files /dev/null and b/img/processor/test_files/trim-border/expected/fit/no-border.jpg differ diff --git a/img/processor/test_files/trim-border/expected/optimise/logo-1.png b/img/processor/test_files/trim-border/expected/optimise/logo-1.png new file mode 100644 index 0000000..f3d4fd0 Binary files /dev/null and b/img/processor/test_files/trim-border/expected/optimise/logo-1.png differ diff --git a/img/processor/test_files/trim-border/expected/optimise/logo-2.png b/img/processor/test_files/trim-border/expected/optimise/logo-2.png new file mode 100755 index 0000000..a4516c2 Binary files /dev/null and b/img/processor/test_files/trim-border/expected/optimise/logo-2.png differ diff --git a/img/processor/test_files/trim-border/expected/optimise/no-border.jpg b/img/processor/test_files/trim-border/expected/optimise/no-border.jpg new file mode 100755 index 0000000..633c5ea Binary files /dev/null and b/img/processor/test_files/trim-border/expected/optimise/no-border.jpg differ diff --git a/img/processor/test_files/trim-border/expected/resize/logo-1.png b/img/processor/test_files/trim-border/expected/resize/logo-1.png new file mode 100755 index 0000000..d4d0c3f Binary files /dev/null and b/img/processor/test_files/trim-border/expected/resize/logo-1.png differ diff --git a/img/processor/test_files/trim-border/expected/resize/logo-2.png b/img/processor/test_files/trim-border/expected/resize/logo-2.png new file mode 100755 index 0000000..69bf4ba Binary files /dev/null and b/img/processor/test_files/trim-border/expected/resize/logo-2.png differ diff --git a/img/processor/test_files/trim-border/expected/resize/no-border.jpg b/img/processor/test_files/trim-border/expected/resize/no-border.jpg new file mode 100755 index 0000000..7d4bea8 Binary files /dev/null and b/img/processor/test_files/trim-border/expected/resize/no-border.jpg differ diff --git a/img/processor/test_files/trim-border/logo-1.png b/img/processor/test_files/trim-border/logo-1.png new file mode 100644 index 0000000..942bf15 Binary files /dev/null and b/img/processor/test_files/trim-border/logo-1.png differ diff --git a/img/processor/test_files/trim-border/logo-2.png b/img/processor/test_files/trim-border/logo-2.png new file mode 100644 index 0000000..3daaea4 Binary files /dev/null and b/img/processor/test_files/trim-border/logo-2.png differ diff --git a/img/processor/test_files/trim-border/no-border.jpg b/img/processor/test_files/trim-border/no-border.jpg new file mode 100644 index 0000000..59cb362 Binary files /dev/null and b/img/processor/test_files/trim-border/no-border.jpg differ diff --git a/img/service.go b/img/service.go index d385b26..402cc9d 100644 --- a/img/service.go +++ b/img/service.go @@ -61,6 +61,8 @@ type TransformationConfig struct { SupportedFormats []string // Quality defines quality of output image Quality Quality + // TrimBorder is a flag whether we need to remove border or not + TrimBorder bool // Config is the configuration for the specific transformation Config interface{} } @@ -147,8 +149,7 @@ func (r *Service) OptimiseUrl(resp http.ResponseWriter, req *http.Request) { } func (r *Service) ResizeUrl(resp http.ResponseWriter, req *http.Request) { - - size := getQueryParam(req.URL, "size") + size, _ := getQueryParam(req.URL, "size") if len(size) == 0 { http.Error(resp, "size param is required", http.StatusBadRequest) return @@ -165,7 +166,7 @@ func (r *Service) ResizeUrl(resp http.ResponseWriter, req *http.Request) { } func (r *Service) FitToSizeUrl(resp http.ResponseWriter, req *http.Request) { - size := getQueryParam(req.URL, "size") + size, _ := getQueryParam(req.URL, "size") if len(size) == 0 { http.Error(resp, "size param is required", http.StatusBadRequest) return @@ -244,11 +245,11 @@ func addHeaders(resp http.ResponseWriter, image *Image) { resp.Header().Add("Cache-Control", fmt.Sprintf("public, max-age=%d", CacheTTL)) } -func getQueryParam(url *url.URL, name string) string { +func getQueryParam(url *url.URL, name string) (string, bool) { if len(url.Query()[name]) == 1 { - return url.Query()[name][0] + return url.Query()[name][0], true } - return "" + return "", url.Query().Has(name) } func getImgUrl(req *http.Request) string { @@ -296,7 +297,7 @@ func (r *Service) transformUrl(resp http.ResponseWriter, req *http.Request, tran } var dppx float64 = 0 - dppxParam := getQueryParam(req.URL, "dppx") + dppxParam, _ := getQueryParam(req.URL, "dppx") if len(dppxParam) != 0 { var err error dppx, err = strconv.ParseFloat(dppxParam, 32) @@ -308,13 +309,27 @@ func (r *Service) transformUrl(resp http.ResponseWriter, req *http.Request, tran var saveDataParam = "" if SaveDataEnabled { - saveDataParam = getQueryParam(req.URL, "save-data") + saveDataParam, _ = getQueryParam(req.URL, "save-data") if len(saveDataParam) > 0 && saveDataParam != "off" && saveDataParam != "hide" { http.Error(resp, "save-data query param must be one of 'off', 'hide'", http.StatusBadRequest) return } } + var trimBorder = false + trimBorderParamValue, trimBorderParamExist := getQueryParam(req.URL, "trim-border") + if trimBorderParamExist { + if len(trimBorderParamValue) == 0 { + trimBorder = true + } else { + var err error + trimBorder, err = strconv.ParseBool(trimBorderParamValue) + if err != nil { + http.Error(resp, "can't parse trim-border param", http.StatusBadRequest) + } + } + } + saveDataHeader := req.Header.Get("Save-Data") Log.Printf("[%s]: Transforming image %s using config %+v\n", req.URL.String(), imgUrl, config) @@ -345,6 +360,7 @@ func (r *Service) transformUrl(resp http.ResponseWriter, req *http.Request, tran Src: srcImage, SupportedFormats: supportedFormats, Quality: getQuality(saveDataHeader, saveDataParam, dppx), + TrimBorder: trimBorder, Config: config, }, Resp: resp, diff --git a/img/service_test.go b/img/service_test.go index 0127e3d..2a1f896 100644 --- a/img/service_test.go +++ b/img/service_test.go @@ -23,6 +23,7 @@ const ( ImgPngOut = "123" ImgLowQualityOut = "12" ImgLowerQualityOut = "1" + ImgBorderTrimmed = "777" EmptyGifBase64Out = "R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" ) @@ -75,6 +76,12 @@ func (r *resizerMock) supports(supportedFormats []string, format string) bool { } func (r *resizerMock) resultImage(config *img.TransformationConfig) *img.Image { + if config.TrimBorder { + return &img.Image{ + Data: []byte(ImgBorderTrimmed), + } + } + if string(config.Src.Data) == NoContentTypeImgSrc { return &img.Image{ Data: []byte(NoContentTypeImgOut), @@ -336,6 +343,32 @@ func TestService_Transforms(t *testing.T) { ) }, }, + { + Description: "Trim Border", + Request: &http.Request{ + Method: "GET", + URL: parseUrl(fmt.Sprintf("http://localhost/img/http%%3A%%2F%%2Fsite.com/img.png%s&trim-border", tt.urlSuffix), t), + }, + Handler: func(w *httptest.ResponseRecorder, t *testing.T) { + test.Error(t, + test.Equal("3", w.Header().Get("Content-Length"), "Content-Length header"), + test.Equal(ImgBorderTrimmed, w.Body.String(), "Resulted image"), + ) + }, + }, + { + Description: "Trim Border False", + Request: &http.Request{ + Method: "GET", + URL: parseUrl(fmt.Sprintf("http://localhost/img/http%%3A%%2F%%2Fsite.com/img.png%s&trim-border=0", tt.urlSuffix), t), + }, + Handler: func(w *httptest.ResponseRecorder, t *testing.T) { + test.Error(t, + test.Equal("3", w.Header().Get("Content-Length"), "Content-Length header"), + test.Equal(ImgPngOut, w.Body.String(), "Resulted image"), + ) + }, + }, { Url: fmt.Sprintf("http://localhost/img/NO_SUCH_IMAGE%s", tt.urlSuffix), ExpectedCode: http.StatusInternalServerError, @@ -497,6 +530,21 @@ func TestService_FitToSizeUrl(t *testing.T) { test.RunRequests(testCases) } +func TestService_TrimBorder(t *testing.T) { + test.Service = createService(t).GetRouter().ServeHTTP + test.T = t + + testCases := []test.TestCase{ + { + Url: "http://localhost/img/http%3A%2F%2Fsite.com/img.png/fit?size=50x50&trim-border=abc", + ExpectedCode: http.StatusBadRequest, + Description: "trim-border param value is invalid", + }, + } + + test.RunRequests(testCases) +} + func TestService_AsIs(t *testing.T) { test.Service = createService(t).GetRouter().ServeHTTP test.T = t diff --git a/swagger.yaml b/swagger.yaml index dfece15..25bd42f 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -72,6 +72,15 @@ components: galaxy8: value: 4 summary: Samsung Galaxy S8 + trim-border: + description: > + Removes the edges of the image that have exactly the same color. + required: false + in: query + name: trim-border + schema: + type: boolean + allowEmptyValue: true security: - ApiKey: [] @@ -91,6 +100,7 @@ paths: - $ref: "#/components/parameters/imgUrl" - $ref: "#/components/parameters/dppx" - $ref: "#/components/parameters/save-data" + - $ref: "#/components/parameters/trim-border" responses: 200: description: An optimised image @@ -126,6 +136,7 @@ paths: - $ref: "#/components/parameters/imgUrl" - $ref: "#/components/parameters/dppx" - $ref: "#/components/parameters/save-data" + - $ref: "#/components/parameters/trim-border" - name: size required: true in: query @@ -176,6 +187,7 @@ paths: - $ref: "#/components/parameters/imgUrl" - $ref: "#/components/parameters/dppx" - $ref: "#/components/parameters/save-data" + - $ref: "#/components/parameters/trim-border" - name: size required: true in: query