Skip to main content

lib/pdf/component/footnote.ex

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

  Renders footnotes at a given position with a separator line,
  superscript numbers, and smaller text.

  ## Examples

      doc |> Pdf.Component.Footnote.render({50, 100}, %{width: 450}, [
        "Source: World Bank Data, 2025",
        "All figures adjusted for inflation",
        "Excluding outlier regions"
      ])
  """

  @default_font "Helvetica"
  @default_font_size 7
  @default_color {0.4, 0.4, 0.4}
  @default_line_color {0.75, 0.75, 0.75}
  @default_line_height 11
  @default_separator_width 80

  @doc """
  Render footnotes at `{x, y}`.

  ## Style options

  - `:width` — available width (default `450`)
  - `:font` — font name (default `"Helvetica"`)
  - `:font_size` — text size (default `7`)
  - `:color` — text color
  - `:line_color` — separator line color
  - `:line_height` — spacing between notes (default `11`)
  - `:separator_width` — width of the top separator line (default `80`)
  - `:start_number` — first footnote number (default `1`)
  """
  def render(doc, {x, y}, style \\ %{}, notes) do
    _width = Map.get(style, :width, 450)
    font = Map.get(style, :font, @default_font)
    font_size = Map.get(style, :font_size, @default_font_size)
    color = Map.get(style, :color, @default_color)
    line_color = Map.get(style, :line_color, @default_line_color)
    line_height = Map.get(style, :line_height, @default_line_height)
    sep_width = Map.get(style, :separator_width, @default_separator_width)
    start_num = Map.get(style, :start_number, 1)

    # Separator line
    doc =
      doc
      |> Pdf.save_state()
      |> Pdf.set_stroke_color(line_color)
      |> Pdf.set_line_width(0.5)
      |> Pdf.line({x, y}, {x + sep_width, y})
      |> Pdf.stroke()
      |> Pdf.restore_state()

    # Footnote entries
    notes
    |> Enum.with_index(start_num)
    |> Enum.reduce(doc, fn {note, num}, d ->
      ny = y - 8 - (num - start_num) * line_height
      num_text = "#{num}"
      num_w = String.length(num_text) * (font_size - 1) * 0.55

      d
      |> Pdf.set_font(font, font_size - 1)
      |> Pdf.set_fill_color(color)
      |> Pdf.text_at({x, ny + 2}, num_text)
      |> Pdf.set_font(font, font_size)
      |> Pdf.set_fill_color(color)
      |> Pdf.text_at({x + num_w + 3, ny}, note)
    end)
  end
end