diff --git a/.tool-versions b/.tool-versions index 18f5606..c9d26e5 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 20.2.2 elixir 1.6.1 +erlang 20.2.2 diff --git a/README.md b/README.md index d4314f7..dfc5841 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,26 @@ name. For example, The WidgetsController#show action would produce: `widgets widgets-show` +### Data URI + +To inline an image, you may use the `Brady.data_uri/1` function. Pass in the +path relative to `priv/static` and Brady will read the file, base64 encode it, and +return the data uri that is compatible to use within an `` tag. + +For example: + +`<%= img_tag(Brady.data_uri("images/placeholder.gif"), alt: "Celery Man" %>` + +It's not recommended to inline images that are more than a few kilobytes in +size. By default, Brady will emit a warning when inlining an image more than +2kb. You can configure this yourself with Mix config: + +```elixir + config :brady, + otp_app: :my_app, + inline_threshold: 10_240 +``` + ### Inline SVG The inline_svg function works by passing in your SVG file name and, optionally, diff --git a/lib/brady.ex b/lib/brady.ex index 650d25b..9d642eb 100644 --- a/lib/brady.ex +++ b/lib/brady.ex @@ -1,5 +1,6 @@ defmodule Brady do alias Phoenix.Controller + require Logger @doc """ Returns the controller name and controller-action name as a lowercase, @@ -38,6 +39,45 @@ defmodule Brady do end end + @doc """ + Encodes an image to base64-encoded data uri, compatible for img src attributes. Only recommended + for files less than 2kb. This threshold is configurable with mix config: + + config :brady, inline_threshold: 10_240 + + Ex: + Brady.data_uri("placeholder.gif") + # => "data:image/gif;base64,iVBORw0KGgoAAAA" + """ + def data_uri(path) do + app_dir = Application.app_dir(Application.get_env(:brady, :otp_app)) + base64 = + [app_dir, "priv/static", path] + |> Path.join() + |> Path.expand() + |> File.read!() + |> Base.encode64() + |> maybe_warn_about_size(path) + + mime = MIME.from_path(path) + + "data:#{mime};base64,#{base64}" + end + + defp maybe_warn_about_size(base64, path) do + limit = Application.get_env(:brady, :inline_threshold, 2048) + + if String.length(base64) > limit do + Logger.warn(""" + Warning: The file "#{path}" is large and not recommended for inlining in templates. Please reconsider inlining this image, or increase the inline threshold by setting: + + config :brady, inline_threshold: size_in_bytes + """) + end + + base64 + end + defp render_with_options(markup, []), do: {:safe, markup} defp render_with_options(markup, options) do markup diff --git a/mix.exs b/mix.exs index 50ef823..eff2299 100644 --- a/mix.exs +++ b/mix.exs @@ -23,7 +23,8 @@ defmodule Brady.Mixfile do {:earmark, "~>0.1", only: :dev}, {:ex_doc, "~> 0.11", only: :dev}, {:floki, "~> 0.13"}, - {:phoenix, "~> 1.2"}, + {:mime, "~> 1.2"}, + {:phoenix, "~> 1.2"} ] end diff --git a/mix.lock b/mix.lock index 1aa0f8d..894e49a 100644 --- a/mix.lock +++ b/mix.lock @@ -2,6 +2,7 @@ "earmark": {:hex, :earmark, "0.2.1", "ba6d26ceb16106d069b289df66751734802777a3cbb6787026dd800ffeb850f3", [:mix], []}, "ex_doc": {:hex, :ex_doc, "0.11.4", "a064bdb720594c3745b94709b17ffb834fd858b4e0c1f48f37c0d92700759e02", [:mix], [{:earmark, "~> 0.1.17 or ~> 0.2", [hex: :earmark, optional: true]}]}, "floki": {:hex, :floki, "0.13.1", "b3b287e02914cb41a66285071dade287165ed1915ab07903e18fb454fe961bad", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, optional: false]}]}, + "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, "mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], []}, "phoenix": {:hex, :phoenix, "1.2.0", "1bdeb99c254f4c534cdf98fd201dede682297ccc62fcac5d57a2627c3b6681fb", [:mix], [{:cowboy, "~> 1.0", [repo: "hexpm", hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [repo: "hexpm", hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [repo: "hexpm", hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [repo: "hexpm", hex: :poison, optional: false]}], "hexpm"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.0", "c31af4be22afeeebfaf246592778c8c840e5a1ddc7ca87610c41ccfb160c2c57", [:mix], [], "hexpm"}, diff --git a/test/brady_test.exs b/test/brady_test.exs index 85ea97e..850ae1b 100644 --- a/test/brady_test.exs +++ b/test/brady_test.exs @@ -1,5 +1,6 @@ defmodule BradyTest do use ExUnit.Case + import ExUnit.CaptureLog alias Plug.Conn doctest Brady @@ -87,4 +88,43 @@ defmodule BradyTest do ~s(This is a test svg)} end end + + describe "data_uri/1" do + test "it returns a base64 encoded string of the given image" do + image = test_asset_path("test/support/test-small.png") + + result = Brady.data_uri(image) + + assert result == "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==" + end + + test "it emits a warning when the file is more than 2kb by default" do + path = test_asset_path("test/support/test-large.png") + + assert capture_log(fn -> + Brady.data_uri(path) + end) =~ """ + Warning: The file "#{path}" is large and not recommended for inlining in templates. Please reconsider inlining this image, or increase the inline threshold by setting: + + config :brady, inline_threshold: size_in_bytes + """ + end + + test "it does not emit a warning when the file is less than configured inline threshold" do + Application.put_env(:brady, :inline_threshold, 99999999) + path = test_asset_path("test/support/test-large.png") + + refute capture_log(fn -> + Brady.data_uri(path) + end) =~ """ + Warning: The file "#{path}" is large and not recommended for inlining in templates. Please reconsider inlining this image. + """ + + Application.delete_env(:brady, :inline_threshold) + end + end + + defp test_asset_path(path) do + "../../../../../../#{path}" + end end diff --git a/test/support/test-large.png b/test/support/test-large.png new file mode 100644 index 0000000..48bc9b4 Binary files /dev/null and b/test/support/test-large.png differ diff --git a/test/support/test-small.png b/test/support/test-small.png new file mode 100644 index 0000000..4bb9a07 Binary files /dev/null and b/test/support/test-small.png differ