lib/pdf417/low_level_encoder.ex

defmodule PDF417.LowLevelEncoder do
  @moduledoc """
  This module takes the output from the HighLevelEncoder and converts it to the integers
  that represent the codewords in binary, and outputs a 2d matrix of those numbers. The codewords
  are stored in clusters.ex, as the standard has three versions used so that no two lines will ever
  repeat the same codeword.
  """

  @start_pattern 0x1FEA8
  @stop_pattern 0x3FA29

  import PDF417.Clusters

  def encode(barcode) do
    barcode
    |> make_matrix()
    |> Enum.map(fn row -> [@start_pattern | convert_to_codewords(row)] ++ [@stop_pattern] end)
  end

  defp convert_to_codewords({row, cluster}) do
    Enum.map(row, fn codeword -> map_code_word(cluster, codeword) end)
  end

  defp make_matrix(config = %{columns: columns, codewords: codewords}) do
    total_rows = div(length(codewords), columns)

    codewords
    |> Enum.chunk_every(columns)
    |> Enum.with_index()
    |> Enum.map(fn {row, row_index} ->
      cluster = rem(row_index, 3)

      [left, right] =
        row_indicators(cluster, total_rows, config)
        |> Enum.map(fn ind -> 30 * div(row_index, 3) + ind end)

      {[left | row] ++ [right], cluster}
    end)
  end

  defp row_indicators(cluster, row_count, %{columns: columns, security_level: security_level}) do
    f1 = div(row_count - 1, 3)
    f2 = columns - 1
    f3 = security_level * 3 + rem(row_count - 1, 3)

    case cluster do
      0 -> [f1, f2]
      1 -> [f3, f1]
      2 -> [f2, f3]
    end
  end
end