defmodule DataMatrix.Encode do alias DataMatrix.SymbolAttribute @ascii_digit 48..57 @ascii_pad 129 @upper_shift 235 @doc """ iex> DataMatrix.Encode.encode("1234569") {1, <<142, 164, 186, 58, 129>>} iex> DataMatrix.Encode.encode("Aa999") {1, <<66, 98, 229, 58, 129>>} iex> DataMatrix.Encode.encode("AAAAAAAAA") {3, <<66, 66, 66, 66, 66, 66, 66, 66, 66, 129, 101, 251>>} """ def encode(input, type \\ :square) when is_binary(input) do encoded = do_encode(input) length = byte_size(encoded) version = find_symbol_version(length, type) capacity = SymbolAttribute.data_capacity(version) {version, encoded <> randomize_253_state(length, capacity - length)} end defp do_encode(<>) when tens in @ascii_digit and unit in @ascii_digit do encoded = 130 + (tens - 48) * 10 + (unit - 48) <> <> do_encode(rest) end defp do_encode(<>) when character in 0..127 do <> <> do_encode(rest) end defp do_encode(<>) when character in 128..255 do encoded = character - 128 + 1 <<@upper_shift>> <> <> <> do_encode(rest) end defp do_encode(<<>>) do <<>> end defp find_symbol_version(data_length, :rectangle) do do_find_symbol_version(data_length, 24) end defp find_symbol_version(data_length, _) do do_find_symbol_version(data_length, 0) end defp do_find_symbol_version(data_length, offset) do version = SymbolAttribute.total_data_codewords() |> Stream.drop(offset) |> Enum.find_index(fn capacity -> capacity >= data_length end) unless version do raise ArgumentError, message: "Message too large" end version + offset end defp randomize_253_state(_, 0) do <<>> end defp randomize_253_state(position, n) do <<@ascii_pad>> <> do_randomize_253_state(position + 1, n - 1) end defp do_randomize_253_state(_, 0) do <<>> end defp do_randomize_253_state(position, n) do pseudo_random = @ascii_pad + rem(149 * position, 253) + 1 pseudo_random = if pseudo_random <= 254 do pseudo_random else pseudo_random - 254 end <> <> do_randomize_253_state(position + 1, n - 1) end end