Pdf.Reader.ObjectResolver (ex_pdf_read v1.0.1)

Copy Markdown View Source

Lazy indirect-object resolver with Map-based cache.

Per the design (sdd/pdf-reader-core/design § 5 "Lazy Resolution Contract"):

  • Cache: a plain Map on %Pdf.Reader.Document{}.cache. No GenServer. Key: {obj_num, gen_num}. Value: the resolved Elixir term.
  • Signature: resolve(doc, {:ref, n, g}) :: {:ok, value, doc} | {:error, reason}. The returned doc carries the updated cache. The caller threads doc forward for cache benefit; dropping the updated doc still yields correct results on the next call (re-parse, same value — the binary is immutable).

  • Idempotent: calling resolve/2 twice on the same ref with the same doc returns the same value. The cache is a hint, not state.

Resolution paths

  1. Cache hit: Map.get(doc.cache, {n, g}) → immediate return.
  2. In-use (classic): look up {n, g} in doc.xref{:in_use, offset, _gen}. Slice binary_part(doc.binary, offset, ...), run Parser.parse_object/1.
  3. Compressed (ObjStm): look up {n, g}{:compressed, objstm_n, index}. Recursively resolve(doc, {:ref, objstm_n, 0}), decode filters, then ObjectStream.fetch/3.
  4. Free / absent: {:error, {:unresolved_ref, {n, g}}}.

Ref chasing

resolve/2 does NOT automatically follow nested refs. If a resolved value is itself {:ref, _, _}, the caller decides whether to chase it. This avoids infinite loops on circular references and keeps the interface predictable.

Summary

Functions

Resolve an indirect object reference to its value.

Functions

resolve(doc, arg)

@spec resolve(Pdf.Reader.Document.t(), {:ref, pos_integer(), non_neg_integer()}) ::
  {:ok, term(), Pdf.Reader.Document.t()} | {:error, term()}

Resolve an indirect object reference to its value.

Returns {:ok, value, updated_doc} on success, where updated_doc has the resolved value cached. Returns {:error, reason} on failure.

The caller should thread the returned doc forward to benefit from caching on subsequent resolutions.

Error reasons

  • {:error, {:unresolved_ref, {n, g}}} — ref is absent from xref or is a free entry.
  • {:error, :malformed} — parse failure.
  • {:error, {:unsupported_filter, name}} — propagated from filter chain.