Document Layout and Reading Order#
This article covers three closely related failure modes in Docling's standard PDF pipeline: (1) form-like pages being misclassified as FORM or KEY_VALUE_REGION regions by the layout model, (2) silent reading-order corruption in mixed-content documents, and (3) how the reading-order model internally handles ContainerElement instances for FORM and KEY_VALUE_REGION clusters.
FORM and KEY_VALUE_REGION as "Wrapper" Types#
The layout model can predict two container labels for form-like regions: DocItemLabel.FORM and DocItemLabel.KEY_VALUE_REGION. The LayoutPostprocessor treats these — along with TABLE and DOCUMENT_INDEX — as wrapper types .
During _process_special_clusters(), any regular cluster whose bounding box is ≥80% contained within a wrapper cluster is captured as a child of that wrapper and removed from the top-level cluster list . For FORM and KEY_VALUE_REGION (but not TABLE or PICTURE), the wrapper's bounding box is then re-fit to the tight union of its children's boxes .
The confidence threshold for both labels is 0.45 — slightly lower than the 0.5 threshold used for TABLE, TEXT, and most others .
Cross-type overlap resolution#
_handle_cross_type_overlaps() implements an important guard: if a KEY_VALUE_REGION (or other wrapper) overlaps a TABLE cluster by >90% and its confidence advantage is <0.1, the wrapper is silently removed in favor of the TABLE . This prevents duplicate coverage but means a region predicted as both a table and a key-value zone will always be surfaced as a TABLE — a common source of unexpected TABLE output on form pages.
Misclassification consequences#
When a form page is classified as FORM (instead of a collection of text/table clusters), the layout postprocessor bundles all child content under the container, and the reading-order model processes it as a ContainerElement rather than individual readable items. The result is fragmented, non-semantic markdown output — spatial label→value bindings are destroyed silently, with no pipeline-level degradation signal .
The same silent-corruption pattern applies when a form page is classified as a single TABLE cluster: OCR'd text items are written to DoclingDocument.texts[] with valid prov.bbox but are never attached to body.children, making them invisible to the markdown serializer .
Reading-Order Processing of Container Elements#
The ReadingOrderModel.__call__() runs four sub-steps: predict_reading_order, predict_to_captions, predict_to_footnotes, and predict_merges .
In _readingorder_elements_to_docling_doc(), the dispatch is type-based :
TextElement→add_text,add_heading,add_list_item, oradd_codeTable→add_table(with a 1×1 rich-cell fallback if the table has children but no predicted structure)FigureElement→add_picture, then_add_child_elementsfor captionsContainerElement(FORM / KEY_VALUE_REGION) →add_groupwithGroupLabel.FORM_AREAorGroupLabel.KEY_VALUE_AREA, then_add_child_elementsto populate children
_add_child_elements() iterates element.cluster.children and adds each child as add_list_item, add_heading, or add_text depending on its label . Children labeled PAGE_HEADER/PAGE_FOOTER receive ContentLayer.FURNITURE; all others receive ContentLayer.BODY .
Key implication: FORM and KEY_VALUE_REGION regions do produce body-reachable items — but via the group's children rather than as top-level body elements. Tools traversing only body.children directly may miss them; the full document tree must be walked.
Reading-Order Corruption in Mixed-Content Documents#
Reading-order errors in documents mixing handwritten + machine-printed text, or documents with rotation skew, are classified as silent semantic corruption: every word has a valid prov.bbox and high OCR confidence, but the word sequence is wrong . These failures are hard to detect because:
- All content is present in
texts[] - No confidence scores are degraded
- A correctness check on individual tokens passes; only the ordering is wrong
The ReadingOrderPredictor (from docling-ibm-models) sorts elements by spatial position. When handwritten and machine-printed segments are processed by different sub-models and merged, or when rotational skew perturbs y-coordinates, the merge step may reorder segments that were continuation parts of the same sentence. There is no per-line reading-order confidence emitted by the current pipeline to surface this .
Debug Visualization#
The layout debug visualizer splits clusters into two panels specifically to expose form/KV classification:
- Left panel: all clusters except
FORM,KEY_VALUE_REGION, andPICTURE - Right panel: only those three types
This side-by-side output (saved as PNG at the configured debug_output_path) is the primary tool for inspecting misclassification of form-like regions. See draw_clusters_and_cells_side_by_side() .
Workarounds#
| Problem | Workaround |
|---|---|
Form page classified as FORM / fragmented output | Use the VLM pipeline (pipeline_cls=VlmPipeline) |
OCR text inside TABLE bbox missing from md_content | Post-process texts[] sorted by (page_no, -bbox.t, bbox.l); or track opt-in PR #3753 |
| Reading-order wrong on mixed or skewed content | No pipeline-level fix yet; surface via per-item prov.bbox inspection |
The VLM pipeline bypasses the classical layout/reading-order stack entirely and is the officially recommended path for documents with heavy form content .
Key Source References#
| File | Purpose |
|---|---|
layout_postprocessor.py | WRAPPER_TYPES, containment thresholds, cross-type overlap resolution |
readingorder_model.py | ContainerElement → FORM_AREA / KEY_VALUE_AREA group dispatch |
| Issue #3653 | Form misclassification symptoms and VLM workaround |
| Issue #3473 | TABLE-region text stranding (zero body.children references) |
| Issue #3723 | Reading-order corruption in mixed handwritten/printed documents |