The logical structure of a tagged document.
An untagged PDF is a picture of a document: the bytes say where every glyph sits, and nothing says which glyphs form a heading, which form a table cell, or what order a human would read them in. Assistive technology has nothing to work with. Tagging adds that missing layer.
Two halves have to agree:
- a structure tree of elements —
/Documentcontaining/H1and/P, a/Tablecontaining/TRcontaining/TD— stored in the document catalog; - marked content in the page's content stream, where drawing operators
are bracketed by
BDC/EMCand stamped with a marked-content id (MCID).
An element points at its MCIDs, and a per-page number tree points back from each MCID to its element. That two-way link is what lets a reader walk the document logically rather than by position.
This module models the tree. See Tincture.tag/4 for the API that builds it.
Container and content elements
Elements divide into two kinds, and the difference decides whether marked content is emitted at all:
- containers (
:document,:table,:tr,:l…) group other elements and never wrap drawing operators directly. They get no MCID, because there is no content of their own to point at. - content elements (
:p,:h1,:td,:figure…) bracket the operators that draw them, and carry an MCID.
Nesting content inside content is legal — a :span inside a :p — and the
innermost open sequence owns whatever is drawn. The parent keeps its own MCID
for everything outside its children.
Summary
Functions
Whether a tag groups other elements rather than wrapping drawn content.
Whether a tag brackets drawing operators and so needs an MCID.
Whether a tree contains anything at all.
Walk a tree of elements depth-first, parents before children.
The PDF structure type name for a tag, e.g. :h1 becomes "H1".
Every tag this module understands, as {atom, pdf_name} pairs.
Types
@type t() :: %{ :tag => tag(), :page_number => pos_integer(), :mcid => non_neg_integer() | nil, :kids => [t()], optional(:alt) => String.t(), optional(:actual_text) => String.t(), optional(:lang) => String.t(), optional(:title) => String.t(), optional(:scope) => :row | :column | :both }
@type tag() :: atom()
Functions
Whether a tag groups other elements rather than wrapping drawn content.
A container gets no marked content and no MCID.
Whether a tag brackets drawing operators and so needs an MCID.
Whether a tree contains anything at all.
Walk a tree of elements depth-first, parents before children.
This is the order elements are written as objects, so an element's id is always lower than its children's.
The PDF structure type name for a tag, e.g. :h1 becomes "H1".
Every tag this module understands, as {atom, pdf_name} pairs.