PdfElixide.Document.Page (pdf_elixide v0.12.0)

Copy Markdown View Source

A handle to one page of a PdfElixide.Document.

A %Page{} is just its document and a zero-based :index — it holds no native resource of its own, which is why there is no Page.close/1 and why building one costs nothing. It stays valid exactly as long as its document does: closing the document makes every page handle taken from it report {:error, %PdfElixide.Error{reason: :closed}}.

Get one from PdfElixide.Document.page/2, all of them from PdfElixide.Document.pages/1, or iterate the document directly — it implements Enumerable over its pages:

for page <- doc, do: PdfElixide.Document.Page.text!(page)

Every extractor PdfElixide.Document offers is available here for a single page, taking the same options. Working a page at a time is also the way to bound memory on a large document, since only one page's results are live at once — see the "Whole-document extraction and memory" section of PdfElixide.Document.

A page whose :index is not a page of the document — only reachable from a hand-built or stale %Page{} — yields %PdfElixide.Error{reason: :out_of_range} from every function here.

Summary

Types

A page's clockwise display rotation in degrees — always one of these four.

t()

Functions

Reads the annotations on the page.

Same as annotations/1 but raises an error if it fails.

Extracts the characters of the page, each with its bounding box, font metadata, and typographic placement.

Same as chars/2 but raises an error if it fails.

Extracts the fonts referenced by the page.

Same as fonts/1 but raises an error if it fails.

Returns whether the page carries a text layer, as opposed to being image-only or blank.

Same as has_text_layer/1 but returns the bare boolean, raising on failure.

Returns the page's height in points.

Same as height/1 but raises an error if it fails.

Extracts the raster images of the page — photos, logos, and scanned pictures.

Same as images/1 but raises an error if it fails.

Lists the Separation and DeviceN ink names the page declares.

Same as inks/2 but raises an error if it fails.

Returns the page's logical page label (e.g. "i", "1", "A-1").

Same as label/1 but raises an error if it fails.

Extracts the straight lines of the page.

Same as lines/1 but raises an error if it fails.

Returns the page's /MediaBox — the sheet it is imposed on — as a PdfElixide.Geometry.Rect in unrotated user space.

Same as media_box/1 but raises an error if it fails.

Extracts the vector paths of the page — lines, curves, rectangles, and shapes.

Same as paths/1 but raises an error if it fails.

Extracts the rectangles of the page.

Same as rects/1 but raises an error if it fails.

Returns the page's /Rotate — the clockwise rotation a viewer applies when displaying it — as 0, 90, 180 or 270.

Same as rotation/1 but raises an error if it fails.

Finds every occurrence of pattern on the page.

Same as search/3 but raises an error if it fails.

Extracts the spans of the page, each a run of text sharing one text state.

Same as spans/2 but raises an error if it fails.

Detects the tables of the page.

Same as tables/2 but raises an error if it fails.

Extracts the text content of the page.

Same as text/2 but raises an error if it fails.

Extracts the text lines of the page, each with its bounding box and words.

Same as text_lines/2 but raises an error if it fails.

Converts the page to an HTML fragment.

Same as to_html/2 but raises an error if it fails.

Converts the page to Markdown.

Same as to_markdown/2 but raises an error if it fails.

Returns the page's width in points.

Same as width/1 but raises an error if it fails.

Extracts the words of the page, each with its bounding box and font metadata.

Same as words/2 but raises an error if it fails.

Types

rotation()

@type rotation() :: 0 | 90 | 180 | 270

A page's clockwise display rotation in degrees — always one of these four.

t()

@type t() :: %PdfElixide.Document.Page{
  doc: PdfElixide.Document.t(),
  index: non_neg_integer()
}

Functions

annotations(page)

@spec annotations(t()) ::
  {:ok, [PdfElixide.Document.Annotation.t()]} | {:error, PdfElixide.Error.t()}

Reads the annotations on the page.

Returns {:ok, []} when the page has no annotations.

annotations!(page)

@spec annotations!(t()) :: [PdfElixide.Document.Annotation.t()]

Same as annotations/1 but raises an error if it fails.

chars(page, opts \\ [])

@spec chars(t(), PdfElixide.Document.chars_opts()) ::
  {:ok, [PdfElixide.Document.Char.t()]} | {:error, PdfElixide.Error.t()}

Extracts the characters of the page, each with its bounding box, font metadata, and typographic placement.

See PdfElixide.Document.chars_opts/0 for the available options.

chars!(page, opts \\ [])

Same as chars/2 but raises an error if it fails.

fonts(page)

@spec fonts(t()) ::
  {:ok, [PdfElixide.Document.Font.t()]} | {:error, PdfElixide.Error.t()}

Extracts the fonts referenced by the page.

Returns {:ok, []} when the page references no fonts — and also when the page or its /Resources could not be read, which PdfElixide.Document.fonts/2 explains.

fonts!(page)

@spec fonts!(t()) :: [PdfElixide.Document.Font.t()]

Same as fonts/1 but raises an error if it fails.

has_text_layer(page)

@spec has_text_layer(t()) :: {:ok, boolean()} | {:error, PdfElixide.Error.t()}

Returns whether the page carries a text layer, as opposed to being image-only or blank.

This is the signal text/1 cannot give: an empty string means "no text was extracted", which a scanned page and a genuinely blank page produce alike. A false here says the page has nothing to extract, so a caller can route it to OCR instead of treating the empty result as content.

It is a static probe, not an extraction — no text is assembled and no fonts are loaded — and it approximates towards true, so the two answers are not equally strong:

  • false is reliable. The page declares no fonts and no form XObjects, or its content stream shows no text and invokes no XObject.
  • true is not a promise that text/1 returns anything. A page whose only XObject holds no text still answers true, and so does a page whose content stream cannot be decoded — deliberately, so that extraction is attempted rather than skipped on a guess.

One asymmetry looks like a bug but is not: a page with no fonts whose sole XObject is an image answers false, which is what makes the image-only case answerable cheaply. Text drawn in invisible render mode (Tr 3) is not considered either way.

has_text_layer?(page)

@spec has_text_layer?(t()) :: boolean()

Same as has_text_layer/1 but returns the bare boolean, raising on failure.

Unlike PdfElixide.Document.has_structure_tree?/1 and PdfElixide.Document.has_xfa?/1, this degrades nothing: every error raises, not just a failure of the handle — see the predicates list in PdfElixide.Error.

Sweep a document with it:

Enum.reject(doc, &PdfElixide.Document.Page.has_text_layer?/1)

height(page)

@spec height(t()) :: {:ok, float()} | {:error, PdfElixide.Error.t()}

Returns the page's height in points.

This is media_box/1's :height, in unrotated user space, and like width/1 it is never negative and is not swapped for a rotated page. See rotation/1.

height!(page)

@spec height!(t()) :: float()

Same as height/1 but raises an error if it fails.

images(page)

@spec images(t()) ::
  {:ok, [PdfElixide.Document.Image.t()]} | {:error, PdfElixide.Error.t()}

Extracts the raster images of the page — photos, logos, and scanned pictures.

Returns {:ok, []} when the page has no images.

images!(page)

@spec images!(t()) :: [PdfElixide.Document.Image.t()]

Same as images/1 but raises an error if it fails.

inks(page, opts \\ [])

@spec inks(t(), PdfElixide.Document.inks_opts()) ::
  {:ok, [String.t()]} | {:error, PdfElixide.Error.t()}

Lists the Separation and DeviceN ink names the page declares.

These are the values :exclude_inks accepts. By default only the page's own /Resources is read; PdfElixide.Document.inks/3 explains what that misses, what :deep costs, and which colorants never appear.

inks!(page, opts \\ [])

@spec inks!(t(), PdfElixide.Document.inks_opts()) :: [String.t()]

Same as inks/2 but raises an error if it fails.

label(page)

@spec label(t()) :: {:ok, String.t()} | {:error, PdfElixide.Error.t()}

Returns the page's logical page label (e.g. "i", "1", "A-1").

This is the human-facing page number the PDF may define, independent of the zero-based physical index. Pages outside any declared label range fall back to their decimal page number.

Every call re-reads the document's label ranges, so use PdfElixide.Document.page_labels/1 to label a whole document.

label!(page)

@spec label!(t()) :: String.t()

Same as label/1 but raises an error if it fails.

lines(page)

@spec lines(t()) ::
  {:ok, [PdfElixide.Document.Path.t()]} | {:error, PdfElixide.Error.t()}

Extracts the straight lines of the page.

The paths of paths/1 narrowed to those classified as single straight segments. Returns {:ok, []} when the page draws none. See the "Rectangles and straight lines" section of PdfElixide.Document.Path for which shapes qualify.

lines!(page)

@spec lines!(t()) :: [PdfElixide.Document.Path.t()]

Same as lines/1 but raises an error if it fails.

media_box(page)

@spec media_box(t()) ::
  {:ok, PdfElixide.Geometry.Rect.t()} | {:error, PdfElixide.Error.t()}

Returns the page's /MediaBox — the sheet it is imposed on — as a PdfElixide.Geometry.Rect in unrotated user space.

The rect is normalized: :x and :y are the bottom-left corner and :width and :height are non-negative, even for a file that writes the two corners in the reverse order. It is not turned to match rotation/1: a 90-degree page of a 612 × 792 MediaBox still reports 612 × 792 and displays 792 points wide.

The origin need not be {0.0, 0.0}, and when it is not, nothing this library returns is rebased on it — a glyph at the left edge of a page whose box starts at 10.0 reports an x near 10.0. /MediaBox is also inheritable (ISO 32000-1 §7.7.3.4): a page without one takes the box from an ancestor /Pages node, and where two ancestors declare one, which of them wins is not stable. Both are covered by the "Page boxes and the coordinate origin" section of PdfElixide.Document.

A page with no /MediaBox anywhere above it, or whose entry is not an array, or is an array of fewer than four elements, is malformed and yields %PdfElixide.Error{reason: :invalid_pdf} — no default page size is substituted. One malformation is not reported: an element that is not a number reads as 0.0, so such a page reports a smaller box rather than failing.

media_box!(page)

@spec media_box!(t()) :: PdfElixide.Geometry.Rect.t()

Same as media_box/1 but raises an error if it fails.

paths(page)

@spec paths(t()) ::
  {:ok, [PdfElixide.Document.Path.t()]} | {:error, PdfElixide.Error.t()}

Extracts the vector paths of the page — lines, curves, rectangles, and shapes.

Returns {:ok, []} when the page has no vector graphics.

paths!(page)

@spec paths!(t()) :: [PdfElixide.Document.Path.t()]

Same as paths/1 but raises an error if it fails.

rects(page)

@spec rects(t()) ::
  {:ok, [PdfElixide.Document.Path.t()]} | {:error, PdfElixide.Error.t()}

Extracts the rectangles of the page.

The paths of paths/1 narrowed to those classified as rectangles. Returns {:ok, []} when the page draws none. See the "Rectangles and straight lines" section of PdfElixide.Document.Path for which shapes qualify.

rects!(page)

@spec rects!(t()) :: [PdfElixide.Document.Path.t()]

Same as rects/1 but raises an error if it fails.

rotation(page)

@spec rotation(t()) :: {:ok, rotation()} | {:error, PdfElixide.Error.t()}

Returns the page's /Rotate — the clockwise rotation a viewer applies when displaying it — as 0, 90, 180 or 270.

A page's own /Rotate wins, but the entry is inheritable (ISO 32000-1 §7.7.3.4): a page without one takes the value from an ancestor /Pages node, and 0 when no ancestor has one either. Where two ancestors declare one, which of them wins is not stable — see the "Page boxes and the coordinate origin" section of PdfElixide.Document, which covers /Rotate as well.

Two normalizations are worth knowing:

  • a value outside 0..359 wraps, so -90 reads as 270;
  • a value that is not a multiple of 90 is invalid per §7.7.3.3 and reads as 0 — it is not rounded down, so 45 is 0, not 90.

media_box/1, and the width/1 and height/1 derived from it, are never swapped to match: a 90-degree page of a 612 × 792 MediaBox displays 792 points wide and 612 tall. Rotation also decides which frame an extracted bbox is expressed in — see "Rotated pages and extracted geometry" in PdfElixide.Document.

rotation!(page)

@spec rotation!(t()) :: rotation()

Same as rotation/1 but raises an error if it fails.

search(page, pattern, opts \\ [])

Finds every occurrence of pattern on the page.

The pattern is plain text unless literal: false is given. See PdfElixide.Document.search_opts/0 for the available options.

search!(page, pattern, opts \\ [])

Same as search/3 but raises an error if it fails.

spans(page, opts \\ [])

@spec spans(t(), PdfElixide.Document.spans_opts()) ::
  {:ok, [PdfElixide.Document.Span.t()]} | {:error, PdfElixide.Error.t()}

Extracts the spans of the page, each a run of text sharing one text state.

See PdfElixide.Document.spans_opts/0 for the available options.

spans!(page, opts \\ [])

Same as spans/2 but raises an error if it fails.

tables(page, opts \\ [])

@spec tables(t(), PdfElixide.Document.tables_opts()) ::
  {:ok, [PdfElixide.Document.Table.t()]} | {:error, PdfElixide.Error.t()}

Detects the tables of the page.

Returns {:ok, []} when the page has no detectable table. See PdfElixide.Document.tables_opts/0 for the available options.

tables!(page, opts \\ [])

Same as tables/2 but raises an error if it fails.

text(page, opts \\ [])

@spec text(t(), PdfElixide.Document.text_opts()) ::
  {:ok, String.t()} | {:error, PdfElixide.Error.t()}

Extracts the text content of the page.

A page that cannot be extracted is an error here, where PdfElixide.Document.text/1 skips it by default — :on_page_error is a whole-document option and does nothing on this path.

See PdfElixide.Document.text_opts/0 for the available options.

text!(page, opts \\ [])

@spec text!(t(), PdfElixide.Document.text_opts()) :: String.t()

Same as text/2 but raises an error if it fails.

text_lines(page, opts \\ [])

@spec text_lines(t(), PdfElixide.Document.text_lines_opts()) ::
  {:ok, [PdfElixide.Document.TextLine.t()]} | {:error, PdfElixide.Error.t()}

Extracts the text lines of the page, each with its bounding box and words.

See PdfElixide.Document.text_lines_opts/0 for the available options.

text_lines!(page, opts \\ [])

Same as text_lines/2 but raises an error if it fails.

to_html(page, opts \\ [])

@spec to_html(t(), PdfElixide.Document.html_opts()) ::
  {:ok, String.t()} | {:error, PdfElixide.Error.t()}

Converts the page to an HTML fragment.

See PdfElixide.Document.html_opts/0 for the available options.

to_html!(page, opts \\ [])

@spec to_html!(t(), PdfElixide.Document.html_opts()) :: String.t()

Same as to_html/2 but raises an error if it fails.

to_markdown(page, opts \\ [])

@spec to_markdown(t(), PdfElixide.Document.markdown_opts()) ::
  {:ok, String.t()} | {:error, PdfElixide.Error.t()}

Converts the page to Markdown.

See PdfElixide.Document.markdown_opts/0 for the available options.

to_markdown!(page, opts \\ [])

@spec to_markdown!(t(), PdfElixide.Document.markdown_opts()) :: String.t()

Same as to_markdown/2 but raises an error if it fails.

width(page)

@spec width(t()) :: {:ok, float()} | {:error, PdfElixide.Error.t()}

Returns the page's width in points.

This is media_box/1's :width, in unrotated user space. Like it, the value is never negative and is not swapped for a rotated page: a page with rotation/1 of 90 or 270 displays as height × width. See rotation/1.

width!(page)

@spec width!(t()) :: float()

Same as width/1 but raises an error if it fails.

words(page, opts \\ [])

@spec words(t(), PdfElixide.Document.words_opts()) ::
  {:ok, [PdfElixide.Document.Word.t()]} | {:error, PdfElixide.Error.t()}

Extracts the words of the page, each with its bounding box and font metadata.

See PdfElixide.Document.words_opts/0 for the available options.

words!(page, opts \\ [])

Same as words/2 but raises an error if it fails.