# `PdfElixide.Document.Page`
[🔗](https://github.com/r8/pdf_elixide/blob/v0.16.0/lib/pdf_elixide/document/page.ex#L1)

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 `%Page{}` — yields
`%PdfElixide.Error{reason: :out_of_range}` from every function here.

# `rotation`

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

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

# `t`

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

# `annotations`

```elixir
@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!`

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

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

# `chars`

```elixir
@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 `t:PdfElixide.Document.chars_opts/0` for the available options.

# `chars!`

```elixir
@spec chars!(t(), PdfElixide.Document.chars_opts()) :: [PdfElixide.Document.Char.t()]
```

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

# `fonts`

```elixir
@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!`

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

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

# `has_text_layer`

```elixir
@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.

One asymmetry looks like a bug but is not: a page with no fonts whose sole
XObject is an *image* answers `false`. Text drawn in invisible render mode
(`Tr 3`) is not considered either way.

# `has_text_layer?`

```elixir
@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 "Errors versus exceptions" section
of `PdfElixide.Error`.

Sweep a document with it:

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

# `height`

```elixir
@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!`

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

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

# `images`

```elixir
@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 — and also in the cases
`PdfElixide.Document.images/2` explains, where an image is left out rather
than reported.

# `images!`

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

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

# `inks`

```elixir
@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!`

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

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

# `label`

```elixir
@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!`

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

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

# `lines`

```elixir
@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!`

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

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

# `media_box`

```elixir
@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`. See "Page boxes and
the coordinate origin" in `PdfElixide.Document` for inherited boxes,
non-zero origins and the coordinate frame.

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!`

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

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

# `paths`

```elixir
@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!`

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

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

# `rects`

```elixir
@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!`

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

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

# `rotation`

```elixir
@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 without `/Rotate` inherits it from an ancestor and otherwise reads as
`0`. See "Page boxes and the coordinate origin" in `PdfElixide.Document` for
the full inheritance behavior.

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`, `width/1` and `height/1` are not swapped to match. Rotation also
affects the frame of some extracted bounding boxes; see "Rotated pages and
extracted geometry" in `PdfElixide.Document`.

# `rotation!`

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

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

# `search`

```elixir
@spec search(t(), String.t(), PdfElixide.Document.search_opts()) ::
  {:ok, [PdfElixide.Document.SearchMatch.t()]} | {:error, PdfElixide.Error.t()}
```

Finds every occurrence of `pattern` on the page.

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

# `search!`

```elixir
@spec search!(t(), String.t(), PdfElixide.Document.search_opts()) :: [
  PdfElixide.Document.SearchMatch.t()
]
```

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

# `spans`

```elixir
@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 `t:PdfElixide.Document.spans_opts/0` for the available options.

# `spans!`

```elixir
@spec spans!(t(), PdfElixide.Document.spans_opts()) :: [PdfElixide.Document.Span.t()]
```

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

# `tables`

```elixir
@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
`t:PdfElixide.Document.tables_opts/0` for the available options.

# `tables!`

```elixir
@spec tables!(t(), PdfElixide.Document.tables_opts()) :: [
  PdfElixide.Document.Table.t()
]
```

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

# `text`

```elixir
@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 `t:PdfElixide.Document.text_opts/0` for the available options, and
`to_plain_text/2` for the other way to read a page as text.

# `text!`

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

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

# `text_lines`

```elixir
@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 `t:PdfElixide.Document.text_lines_opts/0` for the available options.

# `text_lines!`

```elixir
@spec text_lines!(t(), PdfElixide.Document.text_lines_opts()) :: [
  PdfElixide.Document.TextLine.t()
]
```

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

# `to_html`

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

Converts the page to an HTML fragment.

See `t:PdfElixide.Document.html_opts/0` for the available options.

# `to_html!`

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

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

# `to_markdown`

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

Converts the page to Markdown.

See `t:PdfElixide.Document.markdown_opts/0` for the available options.

# `to_markdown!`

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

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

# `to_plain_text`

```elixir
@spec to_plain_text(t(), PdfElixide.Document.plain_text_opts()) ::
  {:ok, String.t()} | {:error, PdfElixide.Error.t()}
```

Converts the page to plain text.

A different assembler from `text/2`; see `PdfElixide.Document.to_plain_text/2`
for how the two differ. See `t:PdfElixide.Document.plain_text_opts/0` for the
available options.

# `to_plain_text!`

```elixir
@spec to_plain_text!(t(), PdfElixide.Document.plain_text_opts()) :: String.t()
```

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

# `width`

```elixir
@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!`

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

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

# `words`

```elixir
@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 `t:PdfElixide.Document.words_opts/0` for the available options.

# `words!`

```elixir
@spec words!(t(), PdfElixide.Document.words_opts()) :: [PdfElixide.Document.Word.t()]
```

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
