From 51cd066a14b8bed8195cc4e1f68f4b22f85249a9 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 5 Aug 2021 12:48:22 +0200 Subject: [PATCH] Add resizing filter to make sure pictures are not too big Closes #810 Signed-off-by: Thomas Citharel --- config/config.exs | 1 + lib/web/upload/filter/resize.ex | 42 ++++++++++++++++++++++++++ lib/web/upload/upload.ex | 6 ++-- test/web/upload/filter/resize_test.exs | 20 ++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 lib/web/upload/filter/resize.ex create mode 100644 test/web/upload/filter/resize_test.exs diff --git a/config/config.exs b/config/config.exs index 177bcc9b..632d7111 100644 --- a/config/config.exs +++ b/config/config.exs @@ -67,6 +67,7 @@ config :mobilizon, Mobilizon.Web.Upload, filters: [ Mobilizon.Web.Upload.Filter.Dedupe, Mobilizon.Web.Upload.Filter.AnalyzeMetadata, + Mobilizon.Web.Upload.Filter.Resize, Mobilizon.Web.Upload.Filter.Optimize ], allow_list_mime_types: ["image/gif", "image/jpeg", "image/png", "image/webp"], diff --git a/lib/web/upload/filter/resize.ex b/lib/web/upload/filter/resize.ex new file mode 100644 index 00000000..424d578a --- /dev/null +++ b/lib/web/upload/filter/resize.ex @@ -0,0 +1,42 @@ +defmodule Mobilizon.Web.Upload.Filter.Resize do + @moduledoc """ + Resize the pictures if they're bigger than maximum size. + + This filter requires `Mobilizon.Web.Upload.Filter.AnalyzeMetadata` to be performed before. + """ + + @behaviour Mobilizon.Web.Upload.Filter + + @maximum_width 1_920 + @maximum_height 1_080 + + def filter(%Mobilizon.Web.Upload{ + tempfile: file, + content_type: "image" <> _, + width: width, + height: height + }) do + file + |> Mogrify.open() + |> Mogrify.resize(string(limit_sizes({width, height}))) + |> Mogrify.save(in_place: true) + + {:ok, :filtered} + end + + def filter(_), do: {:ok, :noop} + + def limit_sizes({width, height}) when width > @maximum_width do + new_height = round(@maximum_width * height / width) + limit_sizes({@maximum_width, new_height}) + end + + def limit_sizes({width, height}) when height > @maximum_height do + new_width = round(@maximum_height * width / height) + limit_sizes({new_width, @maximum_height}) + end + + def limit_sizes({width, height}), do: {width, height} + + defp string({width, height}), do: "#{width}x#{height}" +end diff --git a/lib/web/upload/upload.ex b/lib/web/upload/upload.ex index 5e19cec7..13169ddc 100644 --- a/lib/web/upload/upload.ex +++ b/lib/web/upload/upload.ex @@ -60,9 +60,11 @@ defmodule Mobilizon.Web.Upload do tempfile: String.t(), content_type: String.t(), path: String.t(), - size: integer() + size: integer(), + width: integer(), + height: integer() } - defstruct [:id, :name, :tempfile, :content_type, :path, :size] + defstruct [:id, :name, :tempfile, :content_type, :path, :size, :width, :height] @spec store(source, options :: [option()]) :: {:ok, map()} | {:error, any()} def store(upload, opts \\ []) do diff --git a/test/web/upload/filter/resize_test.exs b/test/web/upload/filter/resize_test.exs new file mode 100644 index 00000000..5faf21ef --- /dev/null +++ b/test/web/upload/filter/resize_test.exs @@ -0,0 +1,20 @@ +defmodule Mobilizon.Web.Upload.Filter.ResizeTest do + use Mobilizon.DataCase, async: true + alias Mobilizon.Web.Upload.Filter.Resize + + test "does not resize if dimensions are ok" do + assert {100, 150} == Resize.limit_sizes({100, 150}) + end + + test "does resize only width if needed" do + assert {1_920, 960} == Resize.limit_sizes({2_000, 1_000}) + end + + test "does resize only height if needed" do + assert {540, 1_080} == Resize.limit_sizes({1_000, 2_000}) + end + + test "does resize if dimentions are really big, and keeps ratio" do + assert {724, 1080} == Resize.limit_sizes({10_050, 15_000}) + end +end