PdfElixide.Error exception (pdf_elixide v0.23.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

:message is a human-readable description.

Errors versus exceptions

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

  • FunctionClauseError when a guard rejects it — a negative page index, an options argument that is not a 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 none of the shapes PdfElixide.Form.put_value/3 accepts. 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.

An ErlangError from any function, predicates included, means the call never reached the document: the native library could not be loaded or called. It is not a condition of the input; report it.

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. As a rule, a false therefore means the answer really is no: a predicate that cannot answer raises. Where a predicate can fail for a reason other than its handle, a counterpart without the ? returns {:ok, boolean} or the error — PdfElixide.Document.Page.has_text_layer/1 and PdfElixide.Editor.marked_for_redaction/2, for example. Predicates that cannot fail, such as PdfElixide.Document.closed?/1, never raise.

The exceptions are PdfElixide.Document.has_structure_tree?/1, PdfElixide.Document.has_xfa?/1 and PdfElixide.Signature.document_timestamp?/1, which treat unreadable content as false. Their counterparts without the ? preserve the error. The first two still raise when the handle itself cannot be used.

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__: term(),
  message: String.t(),
  reason: reason()
}