ExPdfInspector (ExPdfInspector v0.1.0)

Copy Markdown View Source

Inspects PDFs and extracts their content through the pdf-inspector crate.

The functions accept either a file path or the PDF contents as a binary. The result identifies the document type, the pages that need OCR, and, when requested, the extracted Markdown.

{:ok, result} = ExPdfInspector.process_pdf("document.pdf")
result.pdf_type
#=> :text_based

result.markdown
#=> "# Document title

..."

When reading or processing fails, the function returns {:error, error}. The error map contains the :code and :message fields. Error codes are stable library-defined atoms, so callers can safely pattern match on them.

Summary

Types

An error returned while reading or processing a PDF.

A stable code identifying an error returned by the PDF library.

The type of a positioned item extracted from a PDF.

Information about the layout complexity.

The reasons why a page needs OCR.

Markdown extracted from one page.

The result of extracting Markdown by page.

The result of inspecting or processing a PDF.

The type identified for a PDF.

A rectangle associated with an element on a page.

A text item or another element positioned on a page.

Functions

Detects the type of a PDF without extracting Markdown.

Detects the type of a PDF provided as a binary without extracting Markdown.

Extracts Markdown for each page from a PDF file.

Extracts Markdown for each page from a PDF provided as a binary.

Extracts plain text from every page of a PDF.

Extracts text together with its position, font, and style information.

Processes the PDF at path.

Processes a PDF provided as a binary.

Converts plain text to Markdown using the provided options.

Converts positioned items to Markdown.

Converts positioned items to Markdown while considering geometric rectangles.

Types

error()

@type error() :: %{code: error_code(), message: String.t()}

An error returned while reading or processing a PDF.

error_code()

@type error_code() ::
  :io_error | :not_a_pdf | :invalid_structure | :parse_error | :encrypted

A stable code identifying an error returned by the PDF library.

item_type()

@type item_type() :: :text | :image | :form_field | {:link, String.t()}

The type of a positioned item extracted from a PDF.

layout()

@type layout() :: %{
  is_complex: boolean(),
  pages_with_tables: [non_neg_integer()],
  pages_with_columns: [non_neg_integer()]
}

Information about the layout complexity.

ocr_reasons()

@type ocr_reasons() :: %{page: non_neg_integer(), reasons: [String.t()]}

The reasons why a page needs OCR.

page_markdown()

@type page_markdown() :: %{
  page: non_neg_integer(),
  markdown: String.t(),
  needs_ocr: boolean(),
  ocr_reason: String.t() | nil
}

Markdown extracted from one page.

pages_result()

@type pages_result() :: %{
  pages: [page_markdown()],
  pages_with_tables: [non_neg_integer()],
  pages_with_columns: [non_neg_integer()],
  pages_needing_ocr: [non_neg_integer()],
  ocr_reasons_by_page: [ocr_reasons()],
  is_complex: boolean()
}

The result of extracting Markdown by page.

pdf_result()

@type pdf_result() :: %{
  pdf_type: pdf_type(),
  markdown: String.t() | nil,
  page_count: non_neg_integer(),
  processing_time_ms: non_neg_integer(),
  pages_needing_ocr: [non_neg_integer()],
  ocr_reasons_by_page: [ocr_reasons()],
  title: String.t() | nil,
  confidence: float(),
  layout: layout(),
  has_encoding_issues: boolean()
}

The result of inspecting or processing a PDF.

pdf_type()

@type pdf_type() :: :text_based | :scanned | :image_based | :mixed

The type identified for a PDF.

rect()

@type rect() :: %{
  x: float(),
  y: float(),
  width: float(),
  height: float(),
  page: non_neg_integer()
}

A rectangle associated with an element on a page.

result(value)

@type result(value) :: {:ok, value} | {:error, error()}

text_item()

@type text_item() :: %{
  text: String.t(),
  x: float(),
  y: float(),
  width: float(),
  height: float(),
  font: String.t(),
  font_size: float(),
  page: non_neg_integer(),
  is_bold: boolean(),
  is_italic: boolean(),
  is_underline: boolean(),
  is_strikeout: boolean(),
  item_type: item_type(),
  mcid: integer() | nil
}

A text item or another element positioned on a page.

Functions

detect_pdf(path)

@spec detect_pdf(String.t()) :: result(pdf_result())

Detects the type of a PDF without extracting Markdown.

{:ok, result} = ExPdfInspector.detect_pdf("document.pdf")
result.pdf_type
#=> :text_based

detect_pdf_bytes(bytes)

@spec detect_pdf_bytes(binary()) :: result(pdf_result())

Detects the type of a PDF provided as a binary without extracting Markdown.

bytes = File.read!("document.pdf")
{:ok, %{markdown: nil}} = ExPdfInspector.detect_pdf_bytes(bytes)

extract_pages_markdown(path, pages)

@spec extract_pages_markdown(String.t(), [non_neg_integer()] | nil) ::
  result(pages_result())

Extracts Markdown for each page from a PDF file.

Pass nil to extract every page, or pass a list of zero-based page indexes:

{:ok, result} = ExPdfInspector.extract_pages_markdown("document.pdf", [0, 2])
Enum.map(result.pages, & &1.page)
#=> [0, 2]

extract_pages_markdown_bytes(bytes, pages)

@spec extract_pages_markdown_bytes(binary(), [non_neg_integer()] | nil) ::
  result(pages_result())

Extracts Markdown for each page from a PDF provided as a binary.

bytes = File.read!("document.pdf")
{:ok, result} = ExPdfInspector.extract_pages_markdown_bytes(bytes, nil)
length(result.pages)
#=> 3

extract_text(path)

@spec extract_text(String.t()) :: result(String.t())

Extracts plain text from every page of a PDF.

{:ok, text} = ExPdfInspector.extract_text("document.pdf")
String.contains?(text, "Introduction")
#=> true

extract_text_with_positions(path)

@spec extract_text_with_positions(String.t()) :: result([text_item()])

Extracts text together with its position, font, and style information.

{:ok, [item | _]} = ExPdfInspector.extract_text_with_positions("document.pdf")
{item.text, item.page, item.font_size}
#=> {"Title", 1, 24.0}

process_pdf(path)

@spec process_pdf(String.t()) :: result(pdf_result())

Processes the PDF at path.

Without options, it detects and analyzes the document and extracts Markdown.

{:ok, result} = ExPdfInspector.process_pdf("document.pdf")
result.page_count
#=> 3

Pass PdfOptions to control the processing steps:

options = %ExPdfInspector.PdfOptions{mode: :detect_only}
{:ok, result} = ExPdfInspector.process_pdf("document.pdf", options)
result.markdown
#=> nil

process_pdf(path, options)

@spec process_pdf(String.t(), ExPdfInspector.PdfOptions.t()) :: result(pdf_result())

process_pdf_bytes(bytes)

@spec process_pdf_bytes(binary()) :: result(pdf_result())

Processes a PDF provided as a binary.

bytes = File.read!("document.pdf")
{:ok, result} = ExPdfInspector.process_pdf_bytes(bytes)

Processing options can also be provided:

options = %ExPdfInspector.PdfOptions{page_filter: [0]}
{:ok, result} = ExPdfInspector.process_pdf_bytes(bytes, options)

process_pdf_bytes(bytes, options)

@spec process_pdf_bytes(binary(), ExPdfInspector.PdfOptions.t()) ::
  result(pdf_result())

to_markdown(text, options)

@spec to_markdown(String.t(), ExPdfInspector.MarkdownOptions.t()) :: String.t()

Converts plain text to Markdown using the provided options.

options = %ExPdfInspector.MarkdownOptions{profile: :compact}
ExPdfInspector.to_markdown("TITLE

Content", options)

#=> "TITLE

Content "

to_markdown_from_items(items, options)

@spec to_markdown_from_items([text_item()], ExPdfInspector.MarkdownOptions.t()) ::
  String.t()

Converts positioned items to Markdown.

item = %{
  text: "Hello", x: 10.0, y: 20.0, width: 30.0, height: 12.0,
  font: "Helvetica", font_size: 12.0, page: 0,
  is_bold: false, is_italic: false, is_underline: false,
  is_strikeout: false, item_type: :text, mcid: nil
}

ExPdfInspector.to_markdown_from_items([item], %ExPdfInspector.MarkdownOptions{})
#=> "Hello

"

to_markdown_from_items_with_rects(items, options, rects)

@spec to_markdown_from_items_with_rects(
  [text_item()],
  ExPdfInspector.MarkdownOptions.t(),
  [rect()]
) ::
  String.t()

Converts positioned items to Markdown while considering geometric rectangles.

Rectangles help identify visual elements such as underlines:

rect = %{x: 10.0, y: 20.0, width: 80.0, height: 1.0, page: 0}
ExPdfInspector.to_markdown_from_items_with_rects(items, options, [rect])