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()}
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