chunk-engine
Chunking Modes

page_aware

Chunks aligned to pages or slides — and which formats actually have a page number to give you.

View raw

page_aware aligns chunks to page boundaries. It is the mode you reach for when a citation has to say "see p. 12".

Whether it can actually tell you which page depends entirely on the format. Read the next section before you build a page-citation feature on it.

Showing examples for Python
— your choice follows you across the docs.

Which formats have real pages

A page number is only meaningful if the file records one. Three groups:

GroupFormatsWhat you get
Real page identityDOCX (+ DOCM/DOTX/DOTM)page_number on every chunk, plus page_break_type naming the marker it came from
Real page numbers, no break typeDOC (when the file declares hard breaks)page_number on every chunk. It does not emit page_break_type — measured across the .doc corpus, 0 of 26 chunks carry the key
Real slide identityPPTX (+ POTX/POTM/PPSX/PPSM), PPT, ODPslide numbers — slide_range / slide_count (PPTX family), slide_number (PPT, ODP). PPT additionally sets page_number = slide number
Synthesized boundariesPDF, Markdown, HTML, TXT, JSON/JSONL/NDJSON, EML/MBOX, MSG, ODT, RTF, EPUB, IPYNBno page_number. Boundaries are invented by the chunker; page_break_type is heading_boundary or estimated

PDF page_aware does NOT emit a page number

This is the one that surprises people. A PDF obviously has pages, and document_metadata.total_pages reports how many — but the page_aware chunker runs over the extracted markdown, not the page grid, so its chunks carry page_break_type: "estimated" or "heading_boundary" and no page_number key at all. Verified on arxiv_1301.3781_word2vec.pdf (12 pages → 41 chunks, zero of them with a page number).

If you need PDF page citations today, extract per page yourself with list_images / the markdown route, or keep your own page offsets. Do not index chunk["metadata"]["page_number"] for PDF — the key is not there.

Real input → real output (DOCX)

Run against all_round.docx, a five-page Word document, at default parameters:

from py_chunks import get_chunks

# DOCX is one of the few formats whose page_aware chunks carry a real
# page_number. page_break_type says where each boundary came from.
for c in get_chunks("all_round.docx", mode="page_aware"):
    m = c["metadata"]
    print(c["content_type"], "| page", m["page_number"], "|", m["page_break_type"])

# page_aware | page 1 | explicit    <- <w:br w:type="page"/> in the file
# page_aware | page 2 | rendered    <- <w:lastRenderedPageBreak/> hint
# page_aware | page 3 | section     <- <w:sectPr> boundary
# page_aware | page 4 | rendered
# page_aware | page 5 | estimated   <- no marker; paragraphs_per_page fallback

The page_break_type tells you how much to trust each page_number:

ValueSourceReal?
explicit<w:br w:type="page"/> — an author-inserted hard breakyes
sectiona <w:sectPr> section boundaryyes
rendered<w:lastRenderedPageBreak/> — where Word broke the page the last time it laid the document outyes, as of the last save
estimatedno marker in the run; the chunker closed the page after paragraphs_per_page paragraphsno — this one is synthesized

So even within DOCX, a page number is exactly as real as its page_break_type.

For a format with no pages at all, the same mode falls back entirely to synthesized boundaries — here on Markdown:

from py_chunks import get_chunks

# Markdown has no pages, so the engine synthesizes boundaries — page_break_type
# says which rule fired. On a paginated format (docx, pdf, pptx) the same mode
# also carries a real page_number; see the metadata reference.
for c in get_chunks("notes.md", mode="page_aware"):
    print(c["content_type"], "|", c["metadata"]["page_break_type"])

# heading | heading_boundary
# page_aware | heading_boundary
# heading | heading_boundary
# page_aware | heading_boundary
# heading | heading_boundary
# page_aware | heading_boundary

Units and formats

The "page" the chunker builds differs by family:

FamilyUnitBoundary rule
DOCX, DOCparagraphfile's own break markers; paragraphs_per_page only as fallback
PPTX familyslideparagraphs_per_page slides per chunk (see the parameter note below)
PPTslideone chunk per slide
ODPmarkdown blockeach slide renders as a heading, so the heading boundary is the slide boundary; slide_number rides along on every chunk
Markdown, HTML, TXT, PDF, EPUB, RTF, EML/MBOX, MSG, ODT, JSON, IPYNBmarkdown blockheading boundaries, else paragraphs_per_page blocks
Spreadsheets (XLSX family)rowwhole-sheet regions, capped at 2,000 chars → content_type: "sheet_region"
CSV / TSVrowparagraphs_per_page rows per chunk → content_type: "row_group"

Parameters

ParameterDefaultMeaning
paragraphs_per_page15Units per page-sized chunk. Slides per chunk for PPTX; rows per chunk for CSV/TSV. Must be > 0.

`paragraphs_per_page = 0` is rejected in `page_aware` — including the spreadsheets that ignore the value

Zero raises everywhere this mode is available: ValueError in Python, kind: "invalid-arg" in JavaScript, ChunkError::InvalidArg in Rust. The XLSX family is included even though it paginates by whole-sheet regions and never reads the number — paragraphs_per_page=1 and =100 return the same chunks — because silently accepting a value that cannot mean anything is worse than refusing it. The sentence names the knob the format actually uses, so CSV/TSV report rows_per_chunk must be greater than 0.

The check is scoped to page_aware, the only mode that reads the parameter. get_chunks(path, mode="default", paragraphs_per_page=0) is accepted, exactly as window_size=0 is accepted outside sliding_window.

PPTX: there is no `slides_per_chunk` here

Through get_chunks / getChunks / chunks_rs::get_chunks, PPTX receives paragraphs_per_page and interprets it as slides per chunk — so the default is 15, not 5. On an eight-slide deck that produces exactly one chunk. Pass paragraphs_per_page=1 for one chunk per slide.

slides_per_chunk (and the default of 5) exist only on Python's format-specific chunk_pptx() helper, where they are documented as the preferred alias for paragraphs_per_page. There is no such parameter in js-chunks or rs-chunks, and none through the unified entry point in any SDK.

What it emits

page_aware does not emit only page_aware chunks.

Formatcontent_type values emitted
Markdown-family — Markdown, HTML, TXT, PDF, EPUB, RTF, EML/MBOX, MSG, ODT, ODP, JSON, IPYNBpage_aware plus a heading chunk for every heading in the document
DOCX, DOC, PPTX family, PPTpage_aware only — heading text sits inside the page chunk
Spreadsheetssheet_region
CSV / TSVrow_group

JSON/JSONL render no headings of their own, so a heading chunk appears there only if a field's value happens to contain Markdown heading syntax.

Measured: arxiv_1301.3781_word2vec.pdf → 31 page_aware + 10 heading; prose_heavy.md → 10 page_aware + 10 heading; all_round.docx → 5 page_aware and nothing else.

Metadata

Every key this mode writes, per format, is listed in the Metadata Reference — including which formats carry page_number, page_break_type, slide_range, and total_pages.

When to use it

  • Page-referenced citations from DOCX or DOC — the formats that carry a real page number.
  • Slide-scoped chunking of presentations (set paragraphs_per_page=1).
  • A coarse, roughly-even split of a long prose document when you don't care that the boundaries are invented.

On this page