Skip to main content

lib/pdf/component/signature.ex

defmodule Pdf.Component.Signature do
  @moduledoc """
  Signature component for PDF documents.

  Renders a signature line with name, title/role, and optional date.
  Useful for contracts, letters, and formal documents.

  ## Examples

      doc |> Pdf.Component.Signature.render({50, 200}, %{
        name: "John Doe",
        title: "CEO, Acme Corp"
      })

      doc |> Pdf.Component.Signature.render({50, 200}, %{
        name: "Jane Smith",
        title: "Chief Architect",
        date: "May 19, 2026",
        width: 250
      })
  """

  @default_width 200
  @default_line_color {0.3, 0.3, 0.3}
  @default_name_color {0.1, 0.1, 0.1}
  @default_title_color {0.45, 0.45, 0.45}
  @default_font "Helvetica"

  @doc """
  Render a signature block at `{x, y}`.

  ## Style options

  - `:name` — signer name (required)
  - `:title` — role/title below name
  - `:date` — optional date string
  - `:width` — line width (default `200`)
  - `:font` — font name (default `"Helvetica"`)
  - `:line_color` — signature line color
  - `:name_color` — name text color
  - `:title_color` — title/date text color
  - `:label` — label above line (e.g. "Authorized by")
  """
  def render(doc, {x, y}, style \\ %{}) do
    name = Map.get(style, :name, "")
    title = Map.get(style, :title)
    date = Map.get(style, :date)
    label = Map.get(style, :label)
    width = Map.get(style, :width, @default_width)
    font = Map.get(style, :font, @default_font)
    line_color = Map.get(style, :line_color, @default_line_color)
    name_color = Map.get(style, :name_color, @default_name_color)
    title_color = Map.get(style, :title_color, @default_title_color)

    # Optional label above the line
    {doc, line_y} =
      if label do
        doc =
          doc
          |> Pdf.set_font(font, 8)
          |> Pdf.set_fill_color(title_color)
          |> Pdf.text_at({x, y}, label)

        {doc, y - 16}
      else
        {doc, y}
      end

    # Signature line
    doc =
      doc
      |> Pdf.save_state()
      |> Pdf.set_stroke_color(line_color)
      |> Pdf.set_line_width(0.8)
      |> Pdf.line({x, line_y}, {x + width, line_y})
      |> Pdf.stroke()
      |> Pdf.restore_state()

    # Name
    doc =
      doc
      |> Pdf.set_font(font, 10, bold: true)
      |> Pdf.set_fill_color(name_color)
      |> Pdf.text_at({x, line_y - 14}, name)

    # Title
    doc =
      if title do
        doc
        |> Pdf.set_font(font, 9)
        |> Pdf.set_fill_color(title_color)
        |> Pdf.text_at({x, line_y - 26}, title)
      else
        doc
      end

    # Date (right-aligned)
    if date do
      date_w = String.length(date) * 9 * 0.52
      doc
      |> Pdf.set_font(font, 9)
      |> Pdf.set_fill_color(title_color)
      |> Pdf.text_at({x + width - date_w, line_y - 14}, date)
    else
      doc
    end
  end
end