diff --git a/lib/Epub/Epub/blocks/BlockStyle.h b/lib/Epub/Epub/blocks/BlockStyle.h
index a5a616bf..db56f6b7 100644
--- a/lib/Epub/Epub/blocks/BlockStyle.h
+++ b/lib/Epub/Epub/blocks/BlockStyle.h
@@ -22,6 +22,10 @@ struct BlockStyle {
int16_t textIndent = 0;
bool textIndentDefined = false; // true if text-indent was explicitly set in CSS
bool textAlignDefined = false; // true if text-align was explicitly set in CSS
+ // Set when this block was created by a
element. Used by startNewTextBlock to inject
+ // a full line-height gap when the
block stays empty (section-break use case).
+ // NOT propagated through getCombinedBlockStyle so it can't leak into sibling blocks.
+ bool fromBrElement = false;
// Combined horizontal insets (margin + padding)
[[nodiscard]] int16_t leftInset() const { return marginLeft + paddingLeft; }
@@ -58,6 +62,9 @@ struct BlockStyle {
combinedBlockStyle.alignment = alignment;
combinedBlockStyle.textAlignDefined = textAlignDefined;
}
+ // fromBrElement is never propagated — it is consumed by startNewTextBlock
+ // when the empty
block is merged with the following paragraph.
+ combinedBlockStyle.fromBrElement = false;
return combinedBlockStyle;
}
diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp
index b0721607..e9e46fc4 100644
--- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp
+++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp
@@ -133,7 +133,17 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) {
// Merge with existing block style to accumulate CSS styling from parent block elements.
// This handles cases like
(line break only — no extra gap).
+ 5.
at start of chapter (no gap before first paragraph).
+ 6.
following a heading.
+
+Visual verification instructions are embedded as the first paragraph of each
+chapter so a human tester can confirm the expected result on device.
+"""
+
+import os
+import zipfile
+from pathlib import Path
+
+OUTPUT_DIR = Path(__file__).parent.parent / "test" / "epubs"
+OUTPUT_PATH = OUTPUT_DIR / "test_br_section_break.epub"
+
+FILLER = (
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
+ "tempor incididunt ut labore et dolore magna aliqua."
+)
+
+CSS = """\
+body { margin: 0; padding: 0; }
+p { margin-top: 1pt; margin-bottom: 0; text-indent: 1em; text-align: justify; }
+h1 { text-align: center; margin-top: 0.5em; margin-bottom: 0.5em; }
+h2 { text-align: center; margin-top: 0.5em; margin-bottom: 0.5em; }
+.section-br { display: block; }
+"""
+
+def xhtml(title, body):
+ return f"""\
+
+
+
+
PASS: A visible blank-line gap should appear between the two sections below.
+{FILLER}
+{FILLER}
+PASS: The gap above should be roughly one line tall (same as a blank line).
+""") + +# --------------------------------------------------------------------------- +# Chapter 2 —PASS: A blank-line gap should appear between the two sections below, identical +to Ch 1, even though the <br> carries a CSS class.
+{FILLER}
+{FILLER}
+""") + +# --------------------------------------------------------------------------- +# Chapter 3 — multiple consecutivePASS: Two blank lines should appear between the sections (one per <br>).
+{FILLER}
+{FILLER}
+PASS: Three blank lines should appear below.
+{FILLER}
+{FILLER}
+""") + +# --------------------------------------------------------------------------- +# Chapter 4 — inlinePASS: The two lines below should be adjacent with NO extra gap between them. +The <br> is inside the paragraph and must only break the line.
+First line of the paragraph.
Second line of the paragraph — directly below, no gap.
PASS: Above should look like two closely-spaced lines, not like two paragraphs +separated by a blank line.
+""") + +# --------------------------------------------------------------------------- +# Chapter 5 —PASS: There should be a blank-line gap between the heading above and this paragraph.
+{FILLER}
+PASS: There should be a blank-line gap between the section heading and this paragraph.
+""") + +# --------------------------------------------------------------------------- +# Chapter 6 —PASS: This heading should appear near the top of the page with no large blank +area above it despite the <br> being the very first element.
+{FILLER}
+""") + +CHAPTERS = [ + ("ch1", "chapter1.xhtml", "Chapter 1: Standalone br", ch1), + ("ch2", "chapter2.xhtml", "Chapter 2: Classed br", ch2), + ("ch3", "chapter3.xhtml", "Chapter 3: Multiple br", ch3), + ("ch4", "chapter4.xhtml", "Chapter 4: Inline br", ch4), + ("ch5", "chapter5.xhtml", "Chapter 5: br after heading", ch5), + ("ch6", "chapter6.xhtml", "Chapter 6: br at start", ch6), +] + +def build_epub(path): + os.makedirs(os.path.dirname(path), exist_ok=True) + with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as epub: + # mimetype must be first and uncompressed + epub.writestr("mimetype", "application/epub+zip", + compress_type=zipfile.ZIP_STORED) + + epub.writestr("META-INF/container.xml", """\ + +