PdfElixide.Error exception (pdf_elixide v0.12.0)

Copy Markdown View Source

Structured error raised or returned by PdfElixide operations.

Non-bang functions return {:error, %PdfElixide.Error{}}; bang functions raise the same struct (it is an exception). Match on :reason to handle specific failures:

case PdfElixide.Document.open(path) do
  {:ok, doc} -> doc
  {:error, %PdfElixide.Error{reason: :encrypted}} -> prompt_for_password()
  {:error, %PdfElixide.Error{reason: :invalid_pdf}} -> reject()
end

Reasons

  • :encrypted — the PDF is encrypted and needs a password first.
  • :wrong_password — the supplied password was rejected. Comes only from the :password option of open/2, open!/2, from_binary/2 and from_binary!/2; PdfElixide.Document.authenticate/2 reports a wrong password as {:ok, false} instead.
  • :invalid_pdf — malformed or unparseable PDF data.
  • :invalid_pattern — the search pattern could not be parsed. Comes only from PdfElixide.Document.search/2 and friends under literal: false.
  • :unsupported — an unsupported PDF version, feature, or filter.
  • :not_found — a referenced object was not found.
  • :out_of_range — the page index is outside the document.
  • :io — an underlying IO error.
  • :panic — the native library panicked on this input, i.e. hit a bug rather than a condition it reports. The handle stays usable, but a panic partway through an operation can leave it holding partially updated state, so close/1 it and reopen if the error recurs.
  • :lock_poisoned — the internal resource lock was poisoned. Should not occur; a native panic is contained and reported as :panic instead.
  • :closed — the handle was released with close/1.
  • :other — any error not covered above; message is preserved verbatim.

:message is a human-readable description. :details is reserved for future structured payloads and is currently always nil.

Errors versus exceptions

This struct is reserved for PDF and runtime failures. A malformed argument raises instead, even from a non-bang function, because that is a bug in the calling code rather than a condition of the document:

  • FunctionClauseError when a guard rejects it — a negative page index, an options argument that is not a keyword list.
  • ArgumentError for everything else, and the message names the offending key. That covers every way an option can be wrong — an unknown key (detect_heading: for :detect_headings), a key given twice, a value the native layer cannot decode, a value out of range ({:min_overlap, 2.0}) — and undecodable values outside an options map, such as a form field value that is not a tagged tuple. A path is opaque bytes and so has almost nothing to reject — only Windows, which cannot name a file in arbitrary bytes, raises here; see the "File paths" section of PdfElixide.

Build option lists with Keyword.merge/2 rather than ++, since a duplicated key is rejected instead of resolved to the first occurrence. So nothing about the caller arrives as a %PdfElixide.Error{}; if you get one, the arguments were accepted and the document, the filesystem or the handle is what failed.

Predicates ending in ? are the mirror image: they return a bare boolean, so a failure has nowhere to be reported and this struct is raised instead, from a function with no ! in its name. PdfElixide.Document.has_structure_tree?/1 and PdfElixide.Document.has_xfa?/1 answer false for a feature that cannot be read, so only a failure of the handle raises — their strict counterparts PdfElixide.Document.has_structure_tree/1 and PdfElixide.Document.has_xfa/1 return the error instead. PdfElixide.Document.Page.has_text_layer?/1 raises for everything, since answering false would invert the meaning callers act on. PdfElixide.Document.encrypted?/1 asks something that cannot fail, so only the handle can raise, and closed?/1 never raises at all.

Summary

Types

reason()

@type reason() ::
  :encrypted
  | :wrong_password
  | :invalid_pdf
  | :invalid_pattern
  | :unsupported
  | :not_found
  | :out_of_range
  | :io
  | :panic
  | :lock_poisoned
  | :closed
  | :other

t()

@type t() :: %PdfElixide.Error{
  __exception__: true,
  details: map() | nil,
  message: String.t(),
  reason: reason()
}