Skip to content

Commit

Permalink
Add Data URI function (#20)
Browse files Browse the repository at this point in the history
Effort outlined in #19

Introduce a `Brady.data_uri(image_path)` function that will base64 encode the image and output a data uri compatible with `<img src="x">`.

The function will emit warnings when it is inlining images larger than 2048 bytes (after base64 encoding) by default, but can be configured in Mix to have a different threshold.
  • Loading branch information
dbernheisel authored Dec 30, 2018
1 parent cb5abe4 commit 2f803ed
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 20.2.2
elixir 1.6.1
erlang 20.2.2
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<img>` 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,
Expand Down
40 changes: 40 additions & 0 deletions lib/brady.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Brady do
alias Phoenix.Controller
require Logger

@doc """
Returns the controller name and controller-action name as a lowercase,
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down
40 changes: 40 additions & 0 deletions test/brady_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule BradyTest do
use ExUnit.Case
import ExUnit.CaptureLog
alias Plug.Conn
doctest Brady

Expand Down Expand Up @@ -87,4 +88,43 @@ defmodule BradyTest do
~s(<svg class="foo" data-role="bar" height="100" width="100"><desc>This is a test svg</desc><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red"></circle></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
Binary file added test/support/test-large.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/support/test-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2f803ed

Please sign in to comment.