PdfElixide.Document.Page (pdf_elixide v0.23.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, except PdfElixide.Document.running_regions/2, which compares pages and so needs the whole document. The comprehension above collects every page's result. To bound memory used by results, consume pages without accumulating their results — see the streaming example and handle-release guidance in 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.

Summary

Page properties

Returns the page's /CropBox — the region a viewer displays and prints — as a normalized PdfElixide.Geometry.Rect in unrotated user space. It is inherited and reported unclipped, on the same terms as media_box/1.

Same as crop_box/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.

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.

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.

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.

Returns the page's visible region — its /CropBox intersected with its /MediaBox — as a normalized PdfElixide.Geometry.Rect in unrotated user space.

Same as visible_box/1 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.

Text extraction

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 spans of the page, each a run of text sharing one text state.

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

Reads the page as a PdfElixide.Document.StructuredPage containing typed regions.

Same as structured/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 plain text.

Same as to_plain_text/2 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.

Conversion

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.

Graphics, images and annotations

Reads the annotations on the page.

Same as annotations/1 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.

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

Same as images/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.

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.

Rendering

Renders the page to a raster image.

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

Renders a single named ink's plate for the page.

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

Renders one grayscale plate per ink on the page: the process inks and the page's spot inks, painted or not.

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

Types

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

t()

Page properties

crop_box(page)

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

Returns the page's /CropBox — the region a viewer displays and prints — as a normalized PdfElixide.Geometry.Rect in unrotated user space. It is inherited and reported unclipped, on the same terms as media_box/1.

Returns nil when the box is absent or null. A box whose entry is not an array, or is an array of fewer than four elements, yields %PdfElixide.Error{reason: :invalid_pdf}; as with media_box/1, an element that is not a number is not reported and reads as 0.0.

The box is reported as declared, so it may extend past the sheet. Extraction stops at the two intersected — visible_box/1 — not at this box. See What the crop box hides from extraction for what that frame omits.

crop_box!(page)

@spec crop_box!(t()) :: PdfElixide.Geometry.Rect.t() | nil

Same as crop_box/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.

One asymmetry is worth knowing: 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?(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 "Errors versus exceptions" section of 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.

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.

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. See Page boxes and the coordinate origin 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. See crop_box/1 for the part of the sheet a viewer shows.

media_box!(page)

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

Same as media_box/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 without /Rotate inherits it from the nearest ancestor that declares one and otherwise reads as 0. See Which ancestor an inherited box comes from 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.

rotation!(page)

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

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

visible_box(page)

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

Returns the page's visible region — its /CropBox intersected with its /MediaBox — as a normalized PdfElixide.Geometry.Rect in unrotated user space.

Unlike crop_box/1 this always answers a box: the media box stands in whenever the crop box is absent, null, written with fewer than four elements, written with an element that is not a number, or leaves nothing once intersected. Only an unreadable /MediaBox fails, with %PdfElixide.Error{reason: :invalid_pdf}.

This is the frame text extraction stops at — see What the crop box hides from extraction for which extractors it bounds. It is not what render/1,2 draws: on a page with a malformed /CropBox the two part. Measure a raster from the :width and :height of the returned PdfElixide.Document.RenderedPage, and see "What the raster covers" in the Rendering guide.

visible_box!(page)

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

Same as visible_box/1 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.

Text extraction

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.

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.

structured(page, opts \\ [])

Reads the page as a PdfElixide.Document.StructuredPage containing typed regions.

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

structured!(page, opts \\ [])

Same as structured/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.page_text_opts()) ::
  {:ok, String.t()} | {:error, PdfElixide.Error.t()}

Extracts the text content of the page.

:on_page_error is a whole-document option of PdfElixide.Document.text/1; passing it here raises ArgumentError.

See PdfElixide.Document.page_text_opts/0 for the available options, and to_plain_text/2 for the other way to read a page as text.

text!(page, opts \\ [])

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_plain_text(page, opts \\ [])

@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 PdfElixide.Document.plain_text_opts/0 for the available options.

to_plain_text!(page, opts \\ [])

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

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.

Conversion

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.

Graphics, images and annotations

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.

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.

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

images!(page)

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

Same as images/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.

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.

Rendering

render(page, opts \\ [])

Renders the page to a raster image.

See PdfElixide.Document.render/3 for what the raster covers and PdfElixide.Document.render_opts/0 for resolution, format, background, annotations and layer filtering.

rendered = PdfElixide.Document.Page.render!(page, fit: {240, 320})
File.write!("thumb.png", rendered.data)

See the Rendering guide for rendering each page of a document.

render!(page, opts \\ [])

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

separation(page, ink, opts \\ [])

Renders a single named ink's plate for the page.

See PdfElixide.Document.separation/4. An ink the page never paints yields an all-zero plate.

separation!(page, ink, opts \\ [])

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

separations(page, opts \\ [])

@spec separations(t(), PdfElixide.Document.dpi_opts()) ::
  {:ok, [PdfElixide.Document.SeparationPlate.t()]}
  | {:error, PdfElixide.Error.t()}

Renders one grayscale plate per ink on the page: the process inks and the page's spot inks, painted or not.

See PdfElixide.Document.separations/3 for which inks a page yields and PdfElixide.Document.SeparationPlate for how to read a plate.

separations!(page, opts \\ [])

Same as separations/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()
}