Include parent sections in chapter filenames
Preserve outline ancestry when generating chapter slugs so split files retain their section context. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+24
-5
@@ -192,10 +192,17 @@ def render_page_png(
|
||||
|
||||
|
||||
def toc_sections(doc: fitz.Document, level: int) -> list[dict]:
|
||||
"""Return unique page boundaries from the PDF outline at one level."""
|
||||
"""Return unique page boundaries and ancestor titles from the PDF outline."""
|
||||
sections: list[dict] = []
|
||||
seen_pages: set[int] = set()
|
||||
parents: list[str] = []
|
||||
for toc_level, title, page in doc.get_toc():
|
||||
title = title.strip()
|
||||
if not title:
|
||||
continue
|
||||
parents = parents[: toc_level - 1]
|
||||
ancestors = parents.copy()
|
||||
parents.append(title)
|
||||
if toc_level != level or not title.strip():
|
||||
continue
|
||||
page = max(1, min(page, doc.page_count))
|
||||
@@ -205,12 +212,15 @@ def toc_sections(doc: fitz.Document, level: int) -> list[dict]:
|
||||
if page in seen_pages:
|
||||
continue
|
||||
seen_pages.add(page)
|
||||
sections.append({"title": title.strip(), "start_page": page})
|
||||
sections.append(
|
||||
{"title": title, "parents": ancestors, "start_page": page}
|
||||
)
|
||||
if sections:
|
||||
return sections
|
||||
|
||||
# Keep PDFs without bookmarks usable. This fallback only recognizes
|
||||
# numbered headings and is deliberately limited to one boundary per page.
|
||||
heading_path: list[str] = []
|
||||
for page_index, page in enumerate(doc, start=1):
|
||||
text = clean_page_text(page.get_text())
|
||||
if is_toc_page(text):
|
||||
@@ -220,14 +230,23 @@ def toc_sections(doc: fitz.Document, level: int) -> list[dict]:
|
||||
if not match:
|
||||
continue
|
||||
number, title = match.groups()
|
||||
if number.count(".") + 1 != level:
|
||||
heading_level = number.count(".") + 1
|
||||
heading_path = heading_path[: heading_level - 1]
|
||||
heading_path.append(title.strip())
|
||||
if heading_level != level:
|
||||
continue
|
||||
title = title.strip()
|
||||
if not title or not title[0].isalpha() or TOC_LINE.search(title):
|
||||
continue
|
||||
if page_index not in seen_pages:
|
||||
seen_pages.add(page_index)
|
||||
sections.append({"title": title, "start_page": page_index})
|
||||
sections.append(
|
||||
{
|
||||
"title": title,
|
||||
"parents": heading_path[:-1],
|
||||
"start_page": page_index,
|
||||
}
|
||||
)
|
||||
break
|
||||
return sections
|
||||
|
||||
@@ -322,7 +341,7 @@ def convert(
|
||||
{
|
||||
"num": f"{index:02d}",
|
||||
"title": entry["title"],
|
||||
"slug": f"{index:02d}-{slugify(entry['title'])}",
|
||||
"slug": f"{index:02d}-{slugify(' - '.join((*entry['parents'], entry['title'])))}",
|
||||
"pages": pages,
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user