lib/abatap.ex

defmodule Abatap do
  @moduledoc """
  Generate a basic PNG avatar from first name and last name. Largely based on https://github.com/zhangsoledad/alchemic_avatar and code by Sergio Tapia.
  """

  defp to_rgb(color) do
    [r, g, b] = color
    "rgb(#{r},#{g},#{b})"
  end

  defp rgb_to_hex(r, g, b) when r in 0..255 when g in 0..255 when b in 0..255 do
    "~2.16.0B"
    |> List.duplicate(3)
    |> Enum.join()
    |> :io_lib.format([r, g, b])
    |> to_string
  end

  @doc """
  Generates an image using first name and last name of a user using imagemagick's convert command line tool.

  This function was cribbed from Sergio Tapia (https://sergiotapia.com/)

  https://elixirforum.com/t/generate-images-with-name-initials-using-elixir-and-imagemagick/12668

  ## Examples

      ## default options
      # - palette: :google
      # - shape: :square
      # - size: 512
      # - padding: size * 0.546875

      iex> Abatap.create_from_initials("John", "Doe")
      {:ok, "/var/folders/bq/v60646nd7370n3lffmgfh94r00016k/T/JD-google-1731556906832.png"}

      iex> Abatap.create_from_initials("John", "Doe", palette: :google)
      {:ok, "/var/folders/bq/v60646nd7370n3lffmgfh94r00016k/T/JD-google-1731556436238.png"}

      iex> Abatap.create_from_initials("John", "Doe", palette: :iwanthue, shape: :circle)
      {:ok, "/var/folders/bq/v60646nd7370n3lffmgfh94r00016k/T/JD-iwanthue-1731557222647.png"}

      iex> Abatap.create_from_initials("John", "Doe", palette: :google, shape: :squircle, size: 1024, padding: 400)
      {:ok, "/var/folders/bq/v60646nd7370n3lffmgfh94r00016k/T/JD-google-1731556436238.png"}

  """

  def create_from_initials(first_name, last_name, options \\ []) when byte_size(first_name) > 0 and byte_size(last_name) > 0 do
    palette =
      case Keyword.get(options, :palette) do
        nil ->
          :google

        value ->
          value
      end

    shape =
      case Keyword.get(options, :shape) do
        nil ->
          :square

        value ->
          value
      end

    size =
      case Keyword.get(options, :size) do
        nil ->
          512

        value ->
          value
      end

    padding =
      case Keyword.get(options, :padding) do
        nil ->
          trunc(size * 0.546875)
        value ->
          value
      end

    bg_color =
      case palette do
        :google ->
          rgb_list = Abatap.Color.google_random()
          "#" <> "#{rgb_to_hex(Enum.at(rgb_list, 0), Enum.at(rgb_list, 1), Enum.at(rgb_list, 2))}"
        :iwanthue ->
          rgb_list = Abatap.Color.iwanthue_random()
          "#" <> "#{rgb_to_hex(Enum.at(rgb_list, 0), Enum.at(rgb_list, 1), Enum.at(rgb_list, 2))}"
      end

    initials = "#{String.at(first_name, 0)}#{String.at(last_name, 0)}"

    image_path =
      System.tmp_dir!()
      |> Path.join(
        "#{initials}-#{Atom.to_string(palette)}-#{:os.system_time(:milli_seconds)}.png"
      )

    {:ok, avatar} = Image.Text.text!(initials, background_fill_color: bg_color, font_size: size, padding: padding) |> Image.avatar(shape: shape, size: size)

    Image.write!(avatar, image_path)
    {:ok, image_path}

  end
end