From dfbead5cbb801641c16986e4c14ca077db7a551d Mon Sep 17 00:00:00 2001 From: Laveen OC Date: Sun, 8 Mar 2026 11:46:53 +0530 Subject: [PATCH] fix: prevent orphaned headings at page breaks - Add CondPageBreak(1.2in) before each heading to avoid lone headings at the bottom of a page with content flowing to the next - Wrap heading + its decorators (HR, Spacer) in KeepTogether so the heading block is always rendered atomically - Set keepWithNext=True on all heading paragraph styles as an additional ReportLab-level hint Fixes headings being stranded at page bottom with content on next page. --- src/markpdf/renderer.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/markpdf/renderer.py b/src/markpdf/renderer.py index fabb03b..08fa686 100644 --- a/src/markpdf/renderer.py +++ b/src/markpdf/renderer.py @@ -11,6 +11,7 @@ from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet from reportlab.lib.units import inch from reportlab.platypus import ( + CondPageBreak, HRFlowable, Image, KeepTogether, @@ -114,6 +115,7 @@ def create_styles(theme: dict[str, str | None]) -> dict: spaceBefore=hd["sb"], leading=hd["ld"], fontName="Helvetica-Bold", + keepWithNext=True, ) ) @@ -298,22 +300,29 @@ def build_story( ): level = int(btype[1]) text = _fmt(content, theme) - story.append( - Paragraph(text, styles[f"Heading{level}Custom"]) - ) + # Avoid orphaned headings: break page if less than 1.2in remains + story.append(CondPageBreak(1.2 * inch)) if level == 2: - story.append(Spacer(1, 2)) story.append( - HRFlowable( - width="100%", - thickness=0.5, - color=colors.HexColor(theme["border"]), - spaceBefore=0, - spaceAfter=8, - ) + KeepTogether([ + Paragraph(text, styles[f"Heading{level}Custom"]), + Spacer(1, 2), + HRFlowable( + width="100%", + thickness=0.5, + color=colors.HexColor(theme["border"]), + spaceBefore=0, + spaceAfter=8, + ), + ]) ) else: - story.append(Spacer(1, 4)) + story.append( + KeepTogether([ + Paragraph(text, styles[f"Heading{level}Custom"]), + Spacer(1, 4), + ]) + ) elif btype == BLOCK_PARA: story.append(