Dosu LogoDosu Logo
Ask
Join our Discord
defaultPublic
OpenDataLab
Documentsdefault
DOCX Pagination and Page Index Assignment
DOCX Pagination and Page Index Assignment
Type
Topic
Status
Published
Created
Jul 16, 2026
Updated
Jul 16, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

DOCX Pagination and Page Index Assignment#

Unlike PDFs — which have explicit, renderer-defined page boundaries — DOCX files have no inherent page structure in their XML. MinerU simulates pagination by splitting content at section breaks (w:sectPr) during parsing, then assigns sequential page indices when converting raw output into the middle JSON format.


Page Boundary Detection: w:sectPr-Based Splitting#

The DocxConverter in mineru/model/docx/docx_converter.py maintains two state variables to manage page splitting :

  • self.pages: list — the list of all pages (each page is itself a list of block dicts)
  • self.cur_page: list — the currently accumulating page

The initial page is seeded before traversal begins . _walk_linear() then iterates over every top-level body element, dispatching to handlers that append blocks to self.cur_page.

Section break detection in _handle_text_elements()#

_handle_text_elements() checks each paragraph element for a nested w:sectPr :

  1. Empty paragraph with w:sectPr → calls _start_new_page() immediately (before processing), resetting cur_page to a new list and appending it to pages.
  2. Paragraph with text and w:sectPr → sets is_section_end = True, processes the paragraph's content first, then calls _start_new_page() at the end . This ensures the last paragraph before the section break lands on the correct page.

Layout-only section breaks are skipped#

_is_layout_only_section_break() gates the page-split logic. A w:sectPr is treated as layout-only (no new page) when all of the following hold:

  • The paragraph contains no visible text or images
  • The section type is continuous
  • All w:pgMar attributes (header, footer, top, bottom, left, right) are "0"

This prevents margin-adjustment-only continuous sections from spuriously creating empty pages .


Page Index Assignment: result_to_middle_json()#

DocxConverter.convert() returns self.pages via convert_binary(), passing a list-of-lists where each inner list is one page's blocks. The downstream office_docx_analyze() feeds this directly to result_to_middle_json():

office_docx_analyze()
  → convert_binary() # returns converter.pages
  → result_to_middle_json() # assigns page_idx for each page

Inside result_to_middle_json(), page index assignment is straightforward :

for index, page_blocks in enumerate(model_output_blocks_list):
    page_info = blocks_to_page_info(page_blocks, image_writer, index)
    middle_json["pdf_info"].append(page_info)

The index from Python's enumerate() becomes the page_idx field in each page's info dict . Pages are 0-indexed and ordered exactly as they appear in converter.pages, which mirrors document order.

blocks_to_page_info() wraps each page's blocks through MagicModel (which categorizes blocks by type) and produces the final page_info dict with para_blocks, discarded_blocks, and page_idx .


Data Flow Summary#

DOCX XML (w:body)
  ↓ _walk_linear() / _handle_text_elements()
  ↓ w:sectPr → _start_new_page()
converter.pages = [[blocks_p0], [blocks_p1], ...]
  ↓ convert_binary() → office_docx_analyze()
  ↓ result_to_middle_json()
middle_json["pdf_info"] = [
  {"page_idx": 0, "para_blocks": [...], ...},
  {"page_idx": 1, "para_blocks": [...], ...},
  ...
]

The middle JSON format uses pdf_info as the page array key regardless of file type — DOCX output reuses the same schema as the PDF pipeline, enabling unified downstream processing .


Key Source Files#

FileRole
mineru/model/docx/docx_converter.pyw:sectPr detection, _start_new_page(), block accumulation
mineru/model/docx/main.pyconvert_binary() — returns converter.pages
mineru/backend/office/docx_analyze.pyoffice_docx_analyze() — entry point calling both converter and middle JSON builder
mineru/backend/office/model_output_to_middle_json.pyresult_to_middle_json() — assigns page_idx via enumerate()

This mechanism was introduced in PR #4349, which replaced a single flat self.blocks accumulator with the per-page self.pages/self.cur_page model and added section header/footer extraction.

Documents
Ascend NPU Support
Block Type Filtering and Extraction Control
CJK Font Dependencies
Docker Image Architecture
Document Block Processing Pipeline
DOCX Embedded Image Handling
DOCX Pagination and Page Index Assignment
DOCX Parsing
File Type Support
Formula Number Processing
Gradio Interface
Gradio-API Integration
Hardware Accelerator Support
Hybrid Backend Effort Levels
Image Storage and Management
Large Document Memory Management
Local API Server Lifecycle Management
Long-Running Service Stability
Markdown Text Escaping
MFR Prediction
MinerU API Configuration
MinerU API Server
MinerU Doclib System
MinerU Equation Extraction
MinerU FastAPI Pipeline
MinerU Inference Backends
MinerU Intermediate JSON Format
MinerU Markdown Rendering
Model Weight Management
Multi-Column Layout Processing
PaddleOCR Integration
PDF Layout Detection
PDF Text Extraction Filtering
Pipeline Backend Lifecycle Management
Pipeline Backend Processing Window
Pipeline Streaming Architecture
PyTorch MPS Backend
Table Extraction and Processing
Task File Lifecycle Management
VL Mode
vLLM Configuration
vLLM Multimodal Cache
vLLM Worker Health and Recovery
Windows Console Encoding
Windows Multiprocessing
Word Numbering Format Resolution