defmodule Pdf.Reader.Encoding.Differences do @moduledoc """ Applies a PDF `/Differences` array on top of a base encoding override map. Spec reference: PDF 1.7 § 9.6.5.1. ## Format `/Differences` is an array mixing integers and names: [32 /space 65 /A /B /C 200 /uni0024 ...] - An integer `N` sets the current code position to `N`. - Each subsequent name installs that glyph name at position, then increments by 1. ## API apply(base_overrides, differences_array) :: %{integer() => glyph_name :: binary()} The output is a byte→glyph_name map. Codepoint resolution (via AGL or ToUnicode) happens later in the encoding facade (`Pdf.Reader.Encoding`). `/Differences` entries override the base map. Base entries not touched by `/Differences` are preserved. """ @doc """ Applies a `/Differences` array on top of `base`, returning the merged byte→glyph_name override map. `differences` is a list of integers and `{:name, binary()}` tuples, matching the tagged-tuple convention used by the reader's parser. """ @spec apply(%{non_neg_integer() => binary()}, list()) :: %{non_neg_integer() => binary()} def apply(base, differences) when is_map(base) and is_list(differences) do do_apply(differences, base, 0) end defp do_apply([], acc, _pos), do: acc defp do_apply([n | rest], acc, _pos) when is_integer(n) do do_apply(rest, acc, n) end defp do_apply([{:name, name} | rest], acc, pos) when is_binary(name) do do_apply(rest, Map.put(acc, pos, name), pos + 1) end # Handle bare atom/string names if parser uses those shapes defp do_apply([name | rest], acc, pos) when is_binary(name) do do_apply(rest, Map.put(acc, pos, name), pos + 1) end end