lib/pdf/size.ex

defprotocol Pdf.Size do
  @moduledoc false
  def size_of(object)
end

defimpl Pdf.Size, for: BitString do
  def size_of(string), do: byte_size(string)
end

defimpl Pdf.Size, for: Integer do
  def size_of(number), do: Pdf.Size.size_of(Integer.to_string(number))
end

defimpl Pdf.Size, for: Float do
  def size_of(number),
    do: Pdf.Size.size_of(Pdf.Export.to_iolist(number))
end

defimpl Pdf.Size, for: Date do
  def size_of(_date), do: 12
end

defimpl Pdf.Size, for: DateTime do
  def size_of(%{utc_offset: 0}), do: 19
  def size_of(_date), do: 24
end

defimpl Pdf.Size, for: List do
  def size_of([]), do: 0
  def size_of([_ | _] = list), do: Enum.reduce(list, 0, &(Pdf.Size.size_of(&1) + &2))
end

defimpl Pdf.Size, for: Tuple do
  def size_of({:name, string}), do: 1 + Pdf.Size.size_of(string)

  def size_of({:string, string}), do: 2 + Pdf.Size.size_of(string)

  def size_of({:object, number, generation}),
    do: 4 + Pdf.Size.size_of(number) + Pdf.Size.size_of(generation)

  def size_of({:command, [_ | _] = list}), do: length(list) - 1 + Pdf.Size.size_of(list)
  def size_of({:command, command}), do: Pdf.Size.size_of(command)
end