fix: render <br> between paragraphs as a visible section break (#2548)
Co-authored-by: Brooks Ilg <brooksmilg@gmail.com>
This commit is contained in:
@@ -32,6 +32,11 @@ struct BlockStyle {
|
||||
bool isRtl = false; // true if resolved direction is RTL
|
||||
bool directionDefined = false; // true if direction was explicitly set in CSS/HTML
|
||||
|
||||
// Set when this block was created by a <br> element. Used by startNewTextBlock to inject
|
||||
// a full line-height gap when the <br> block stays empty (section-break use case).
|
||||
// NOT propagated through getCombinedBlockStyle so it can't leak into sibling blocks.
|
||||
bool fromBrElement = false;
|
||||
|
||||
// Combined insets (margin + padding)
|
||||
[[nodiscard]] int16_t leftInset() const { return marginLeft + paddingLeft; }
|
||||
[[nodiscard]] int16_t rightInset() const { return marginRight + paddingRight; }
|
||||
@@ -92,6 +97,9 @@ struct BlockStyle {
|
||||
result.directionDefined = true;
|
||||
}
|
||||
|
||||
// fromBrElement is consumed by startNewTextBlock when an empty <br> block
|
||||
// is merged with the following paragraph; never propagate it further.
|
||||
result.fromBrElement = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +239,16 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) {
|
||||
// open. Merge those into the new style so the first child in a container inherits
|
||||
// the container's vertical spacing.
|
||||
const auto style = currentTextBlock->getBlockStyle();
|
||||
currentTextBlock->setBlockStyle(style.getCombinedBlockStyle(blockStyle, BlockStyle::CombineAxis::Vertical));
|
||||
BlockStyle incoming = blockStyle;
|
||||
if (style.fromBrElement) {
|
||||
// The empty block was created by a <br> section separator. Inject a full line of
|
||||
// blank space before the following paragraph so the scene/section break is visible.
|
||||
// This only fires when the <br> block stayed empty (i.e. no inline text was added).
|
||||
const int16_t lineHeight = static_cast<int16_t>(renderer.getLineHeight(fontId) * lineCompression + 0.5f);
|
||||
incoming.marginTop = static_cast<int16_t>(incoming.marginTop + lineHeight);
|
||||
}
|
||||
|
||||
currentTextBlock->setBlockStyle(style.getCombinedBlockStyle(incoming, BlockStyle::CombineAxis::Vertical));
|
||||
|
||||
flushPendingAnchor();
|
||||
return;
|
||||
@@ -855,7 +864,14 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
||||
// flush word preceding <br/> to currentTextBlock before calling startNewTextBlock
|
||||
self->flushPartWordBuffer();
|
||||
}
|
||||
self->startNewTextBlock(self->blockStyleStack.back().withoutBottom());
|
||||
// Tag the new block so startNewTextBlock can inject a full line-height gap if
|
||||
// the block remains empty (i.e. <br> is a section separator between paragraphs).
|
||||
// If the block gets text added before the next block opens it becomes non-empty,
|
||||
// goes through makePages() normally, and the flag has no effect (inline <br> case).
|
||||
BlockStyle brStyle =
|
||||
self->currentTextBlock ? self->currentTextBlock->getBlockStyle() : self->blockStyleStack.back();
|
||||
brStyle.fromBrElement = true;
|
||||
self->startNewTextBlock(brStyle);
|
||||
} else {
|
||||
self->currentCssStyle = cssStyle;
|
||||
const auto accumulated = self->blockStyleStack.back().getCombinedBlockStyle(userAlignmentBlockStyle,
|
||||
|
||||
Reference in New Issue
Block a user