Multi-Page Table and Element Handling#
RAGFlow must correctly store and display PDF chunks that span multiple pages — most prominently tables whose rows continue across a page break. This involves two separate concerns: the backend reconstructing accurate per-page bounding boxes from MinerU's output, and the frontend scrolling the PDF viewer to every page a chunk covers.
Backend: MinerU Cross-Page Table Reconstruction#
Root Cause#
MinerU's content_list.json merges a cross-page table into a single item but retains only the first fragment's bbox and page_idx . Before the fix in PR #16283, RAGFlow emitted a single position tag referencing only that first-page bbox, causing highlights to appear displaced for tables spanning pages.
The Fix (PR #16283)#
MinerU's middle.json — already requested via return_middle_json: True in the API call — retains the individual per-page table fragments with absolute-pixel coordinates. PR #16283 added the following methods to MinerUParser :
| Method | Purpose |
|---|---|
_read_middle_json() | Locates and loads *_middle.json; gracefully skips if absent |
_normalize_bbox() | Converts middle.json's absolute-pixel coords back to 0–1000 normalized space |
_build_table_fragment_index() | Maps each page index to its sorted list of normalized table-fragment bboxes |
_table_page_chain() | Returns ordered (page, fragment) pairs for a cross-page table using continuity heuristics |
_scale_tag() | Renders a single @@page\tx0\tx1\ttop\tbott## position tag with coordinate conversion |
_page_dims() | Returns page dimensions with three-level fallback (page image → captured size → A4 default) |
Chain detection heuristic: a fragment continues onto the next page when it ends near the bottom (≥ 850/1000 normalized) and the next page has a matching fragment starting near the top (≤ 150/1000) . When middle.json is absent, unreadable, or has an unexpected schema, the parser falls back transparently to the original single-fragment output.
Coordinate Conversion in _line_tag#
_line_tag() converts MinerU's 0–1000 normalized bbox values to pixel coordinates by scaling against rendered page image size (bbox_value / 1000.0 × page_dimension). Inverted coordinates (x0 > x1, top > bottom) are swapped before conversion . When page images are unavailable, _page_dims() falls back through captured page sizes to a 595×842 pt A4 default, ensuring output is always in display space rather than raw normalized values .
Frontend: pdf-preview.tsx Scroll Limitation#
Root Cause#
buildChunkHighlights() correctly creates one IHighlight per position_int entry (one per page). However, the useEffect in PdfPreview only called ref.current(state[0]), scrolling to the first highlight only. For a cross-page chunk, subsequent pages' highlights were rendered but remained off-screen and unreachable without manual navigation .
The Fix (PR #16740)#
PR #16740 replaced the single-timer with a staggered multi-scroll approach :
firstHighlightPerPage(state)— new helper indocument-util.tsthat deduplicates highlights to one per distinctpageNumber.- Staggered
setTimeoutcalls — schedules onescrollToper page at100 ms × (index + 1)intervals, bringing each page's highlight into view in sequence. - Single-page chunk behavior is unchanged (one highlight → one scroll at 100 ms).
Remaining Limitation: Page 1 Dimensions Applied Globally#
PdfPreview fetches viewport dimensions from page 1 only and passes those same width/height values to every IHighlight. For PDFs with non-uniform page sizes, highlights on other pages are rendered with page 1's dimensions — a known unfixed limitation .
Data Flow Summary#
MinerU API
├── content_list.json → single merged table item (first-page bbox only)
└── middle.json → per-page table fragments with absolute bboxes
│
▼
MinerUParser._build_table_fragment_index()
MinerUParser._table_page_chain()
│ (one _scale_tag per page)
▼
position_tag: "@@85\t...\t...##@@86\t...\t...##"
│
▼
position_int: [[85, x0, x1, top, bot], [86, x0, x1, top, bot]]
│
▼
buildChunkHighlights() → [IHighlight(page=85), IHighlight(page=86)]
│
▼
PdfPreview: firstHighlightPerPage() + staggered scrollTo
Key Files#
| File | Role |
|---|---|
deepdoc/parser/mineru_parser.py | MinerU parser: _line_tag, _build_table_fragment_index, _table_page_chain |
web/src/components/document-preview/pdf-preview.tsx | PDF viewer: scrollRef, useEffect scroll-to-highlight |
web/src/utils/document-util.ts | buildChunkHighlights, firstHighlightPerPage |
Related Issues & PRs#
- Issue #16735 — Bug report: cross-page table only highlights first page
- PR #16283 — Backend fix: reconstruct per-page positions from
middle.json - PR #16740 — Frontend fix: staggered scroll to all pages of cross-page chunk