Page Assembly Pipeline#
Overview#
The page assembly stage is stage 5 in Docling's StandardPdfPipeline. It runs per-page inside a ThreadedPipelineStage (batch size 1) and converts the raw LayoutPrediction clusters — produced by the layout model — into typed PageElement objects, then writes the result to page.assembled. No document-level relationships (reading order, heading hierarchy, cross-page merges) are established here; those happen downstream in _assemble_document.
Entry point: PageAssembleModel.__call__() in docling/models/stages/page_assemble/page_assemble_model.py.
Cluster-to-Element Conversion#
__call__() iterates page.predictions.layout.clusters and dispatches by label:
| Label set | Output type | Notes |
|---|---|---|
TEXT_ELEM_LABELS | TextElement | Text assembled from cluster.cells via sanitize_text() |
TABLE_LABELS | Table | Pulled from page.predictions.tablestructure.table_map; empty fallback if absent |
FIGURE_LABEL | FigureElement | Pulled from page.predictions.figures_classification.figure_map; empty fallback |
CONTAINER_LABELS (FORM, KEY_VALUE_REGION) | ContainerElement | Wrapper only; no text extraction |
Elements are collected into three parallel lists on AssembledUnit: elements (all), body (non-headers), and headers (PAGE_HEADER / PAGE_FOOTER clusters). The page.assembled field is set at the end of each page .
Text Sanitization (sanitize_text)#
sanitize_text(lines) processes the list of text lines extracted from cluster.cells. Transformations run in this order:
-
Dehyphenation — if a line ends with
-and both the preceding word fragment and the next line's first word are alphanumeric, the hyphen is stripped and the two fragments are joined without a space . Lines that don't end with-get a trailing space appended before joining. -
Typographic normalization — fraction slash (
⁄→/), curly quotes ('/'→',"/"→"), and bullets (•→·) are replaced with ASCII equivalents . -
Ligature expansion —
_LIGATURE_REmatches codepoints in the Unicode Alphabetic Presentation Forms block (U+FB00–U+FB06) plus Dutch IJ (U+0132/U+0133) and a Private-Use Area discard glyph (U+F0A0), then substitutes via_LIGATURE_MAP:Codepoints Expansion Behaviour U+FB00–U+FB06 ( ff fi fl ffi ffl ſt st)ff fi fl ffi ffl st stSpurious space before next word character is absorbed U+0132 / U+0133 ( IJ/ij)IJ/ijTrailing space preserved (word boundaries kept) U+F0A0 (empty) PUA glyph discarded
Limitation: Ligatures encoded as raw glyph names in the PDF (never resolved to Unicode by the PDF parser) are not normalized by this path.
Hyperlink Matching#
For each text cluster, _match_hyperlink(cluster_bbox, page) scans page.parsed_page.hyperlinks, accumulates coverage (intersection-over-self) per URI across all annotation rectangles that may span a wrapped URL, and returns the best URI only if it covers ≥ 50% of the cluster's bounding box (_HYPERLINK_COVERAGE_THRESHOLD = 0.5). Coordinate systems are reconciled: hyperlink rects are BOTTOMLEFT-origin and are converted to TOPLEFT-origin before comparison . The resolved link is stored on the TextElement.hyperlink field .
AssembledUnit Data Structure#
Defined in base_models.py:
class AssembledUnit(BaseModel):
elements: list[PageElement] = [] # all elements, in cluster order
body: list[PageElement] = [] # non-header elements
headers: list[PageElement] = [] # PAGE_HEADER / PAGE_FOOTER elements
PageElement is a union type: Union[TextElement, Table, FigureElement, ContainerElement] .
The Page model field assembled: AssembledUnit | None starts as None and is populated by PageAssembleModel . After assembly, a postprocess hook frees the image cache and unloads the page backend (unless enrichment stages need them) .
What Happens Next#
page.assembled feeds into _assemble_document() (sequential, after all pages complete), which:
- Aggregates all per-page
AssembledUnitelements - Runs
ReadingOrderModelto establish ordering, caption/footnote attachments, and cross-page text merges (soft-hyphen handling) - Runs
HeadingHierarchyModelfor heading level inference - Produces the final
DoclingDocument
Key Files#
| File | Purpose |
|---|---|
docling/models/stages/page_assemble/page_assemble_model.py | PageAssembleModel, sanitize_text(), _match_hyperlink(), _LIGATURE_MAP |
docling/datamodel/base_models.py | AssembledUnit, Page, PagePredictions, TextElement, Table, FigureElement, ContainerElement |