Packages
image
0.4.0
0.72.0
0.71.0
0.70.0
0.69.0
0.68.0
0.67.0
0.67.0-dev
retired
0.66.0
0.65.0
0.64.0
0.63.0
0.62.1
0.62.0
0.61.1
0.61.0
0.60.0
0.59.3
0.59.2
0.59.1
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.2
0.55.1
retired
0.55.0
0.54.4
0.54.3
0.54.2
0.54.1
0.54.0
0.53.0
0.52.3
0.52.2
0.52.1
0.52.0
retired
0.51.0
0.50.0
0.49.0
0.48.1
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.2
0.43.1
0.43.0
0.42.0
0.41.0
0.40.0
0.39.3
0.39.2
0.39.1
0.39.0
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.0
0.36.2
0.36.1
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.31.0
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
retired
0.19.0
0.18.1
0.18.0
0.17.0
0.16.0
0.15.0
0.14.4
0.14.2
0.14.1
0.14.0
retired
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.10.0-rc.0
retired
0.9.0
retired
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
An approachable image processing library primarily based upon Vix and libvips that is NIF-based, fast, multi-threaded, pipelined and has a low memory footprint.
Current section
Files
Jump to
Current section
Files
lib/image/options/text.ex
defmodule Image.Options.Text do
@moduledoc false
alias Image.Color
alias Vix.Vips.Image, as: Vimage
def default_options do
[
font: "Helvetica",
font_size: 50,
text_fill_color: :white,
text_stroke_color: :none,
text_stroke_width: 1,
font_weight: :normal,
background_fill_color: :none,
background_stroke_color: :none,
background_stroke_width: 1,
background_stroke_opacity: 0.7,
background_fill_opacity: 0.7,
padding: [0, 0],
x: :center,
y: :middle
]
end
@doc """
Validate the options for `Image.Text.render/3`.
"""
def validate_options(options) do
options =
Keyword.merge(default_options(), options)
options =
case Enum.reduce_while(options, options, &validate_option(&1, &2)) do
{:error, value} ->
{:error, value}
options ->
{:ok, options}
end
case options do
{:ok, options} ->
options
|> Map.new()
|> ensure_background_color_if_transparent_text()
|> wrap(:ok)
other -> other
end
end
defp validate_option({:x, x}, options) when is_integer(x) and x >= 0 do
{:cont, options}
end
defp validate_option({:y, y}, options) when is_integer(y) and y >= 0 do
{:cont, options}
end
defp validate_option({:x, x}, options) when x in [:left, :center, :right] do
{:cont, options}
end
defp validate_option({:y, y}, options) when y in [:top, :middle, :bottom] do
{:cont, options}
end
defp validate_option({:font, font}, options) when is_binary(font) do
{:cont, options}
end
defp validate_option({:font_weight, font_weight}, options)
when is_binary(font_weight) or is_atom(font_weight) do
{:cont, options}
end
defp validate_option({:font_weight, font_weight}, options)
when is_integer(font_weight) and font_weight in 1..1000 do
{:cont, options}
end
defp validate_option({:font_size, font_size}, options)
when is_integer(font_size) and font_size >= 0 do
{:cont, options}
end
defp validate_option({:background_fill_color = option, color}, options) do
validate_color(option, color, options)
end
defp validate_option({:background_stroke_color = option, color}, options) do
validate_color(option, color, options)
end
defp validate_option({:text_fill_color = option, color}, options) do
validate_color(option, color, options)
end
defp validate_option({:text_stroke_color = option, color}, options) do
validate_color(option, color, options)
end
defp validate_option({:background_fill_opacity = option, opacity}, options) do
validate_opacity(option, opacity, options)
end
defp validate_option({:background_stroke_opacity = option, opacity}, options) do
validate_opacity(option, opacity, options)
end
defp validate_option({:text_stroke_width = option, width}, options) do
validate_stroke_width(option, width, options)
end
defp validate_option({:background_stroke_width = option, width}, options) do
validate_stroke_width(option, width, options)
end
defp validate_option({:padding, [left, right]}, options)
when is_integer(left) and is_integer(right) and left >= 0 and right >= 0 do
{:cont, options}
end
defp validate_option({:padding = option, padding}, options)
when is_integer(padding) and padding > 0 do
{:cont, Keyword.put(options, option, [padding, padding])}
end
defp validate_option({:padding = option, %Vimage{} = image}, options) do
padding_left = div(Image.width(image), 2)
padding_top = div(Image.height(image), 2)
{:cont, Keyword.put(options, option, [padding_left, padding_top])}
end
defp validate_option(option, _options) do
{:halt, {:error, invalid_option(option)}}
end
@doc false
def validate_color(option, color, options) do
cond do
(is_binary(color) or is_atom(color)) && Map.get(Color.color_map(), Color.normalize(color)) ->
{:cont, options}
match?(<<"#", _rest::bytes-6>>, color) ->
{:cont, options}
match?([_r, _g, _b], color) ->
[r, g, b] = color
hex_color =
["#", convert_color(r), convert_color(g), convert_color(b)]
|> :erlang.iolist_to_binary()
{:cont, Keyword.put(options, option, hex_color)}
color in [:none, "none"] ->
{:cont, Keyword.put(options, option, :transparent)}
color in [:transparent, "transparent"] ->
{:cont, Keyword.put(options, option, :transparent)}
(is_binary(color) or is_atom(color)) && String.downcase(to_string(color)) in ["none", ""] ->
{:cont, Keyword.put(options, option, :none)}
true ->
{:halt, {:error, invalid_option(option, color)}}
end
end
@doc false
def validate_opacity(_option, opacity, options)
when is_float(opacity) and opacity >= 0.0 and opacity <= 1.0 do
{:cont, options}
end
def validate_opacity(option, opacity, _options) do
{:halt, {:error, invalid_option(option, opacity)}}
end
@doc false
def validate_stroke_width(_option, width, options) when is_integer(width) and width > 0 do
{:cont, options}
end
def validate_stroke_width(option, width, _options) do
{:halt, {:error, invalid_option(option, width)}}
end
@doc false
def invalid_option(option) do
"Invalid option or option value: #{inspect(option)}"
end
@doc false
def invalid_option(option, value) do
"Invalid option or option value: #{option}: #{inspect value}"
end
defp ensure_background_color_if_transparent_text(options) do
case options do
%{text_fill_color: :transparent, background_color: :none} ->
Map.put(options, :background_color, "black")
_other ->
options
end
end
defp convert_color(c) do
c
|> round()
|> Integer.to_string(16)
|> String.pad_leading(2, "0")
end
@doc false
def wrap(term, atom) do
{atom, term}
end
end