From 741dd89ac152e270ad626eb6834c76114d17a96c Mon Sep 17 00:00:00 2001 From: Rob Hooper Date: Mon, 27 Apr 2026 12:37:41 -0400 Subject: [PATCH] fix: cap per-side horizontal CSS inset at 2em (#1694) ## Summary - **What is the goal of this PR?** Fix chapter-opener text collapsing to 1-2 words per line in EPUBs that apply large em-based horizontal CSS insets. - **What changes are included?** Caps `marginLeft`, `marginRight`, `paddingLeft`, and `paddingRight` at 2em in `BlockStyle::fromCssStyle` (`lib/Epub/Epub/blocks/BlockStyle.h`). Vertical margins/padding are unchanged - the bug is horizontal-only. ## Additional Context **Repro:** *Mother Night* by Kurt Vonnegut, Chapter 21 ("My best friend..."). The chapter-opening element's embedded CSS sets a large horizontal inset. - Settings: Embedded Style = On, Justify alignment (default). - Before: 1-2 words per line with a visible river between them. - After: body text fills the usable page width like every other paragraph. **Why the clamp lives in `fromCssStyle`:** This is the single point where CSS lengths resolve to pixels, so clamping here keeps both `effectiveWidth` call sites in `ChapterHtmlSlimParser` (`:1131-1133` makePages, `:841-844` long-block split) consistent with the `leftInset()` xOffset. A width-only clamp at either call site would leave text pushed right by a large xOffset. **Test plan:** - [x] Flashed to Xteink X4. Chapter 21 of *Mother Night* renders normally with Embedded Style on. - Users with cached layouts of affected books need to delete `.crosspoint/epub_/sections/` (or the whole `.crosspoint/`) on the SD card to pick up the fix. **Before / After:**

IMG_0320 IMG_0324

--- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_ I used Claude Code (Opus 4.7) to evaluate the codebase and find the relevant code related to this rendering. From there, it was human designed, reviewed and tested. Co-authored-by: rhoopr <> --- lib/Epub/Epub/blocks/BlockStyle.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Epub/Epub/blocks/BlockStyle.h b/lib/Epub/Epub/blocks/BlockStyle.h index a5a616bf9..4166fceb6 100644 --- a/lib/Epub/Epub/blocks/BlockStyle.h +++ b/lib/Epub/Epub/blocks/BlockStyle.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "Epub/css/CssStyle.h" @@ -8,6 +9,12 @@ * BlockStyle - Block-level styling properties */ struct BlockStyle { + // Upper bound (in em) for any single side's horizontal margin or padding. + // Some EPUBs apply huge em-based insets to chapter-opener classes; without a + // cap, effectiveWidth collapses to 1-2 words per line and justification dumps + // the remaining space into a single gap. + static constexpr float MAX_HORIZONTAL_INSET_EM = 2.0f; + CssTextAlign alignment = CssTextAlign::Justify; // Spacing (in pixels) @@ -68,16 +75,17 @@ struct BlockStyle { const uint16_t viewportWidth = 0) { BlockStyle blockStyle; const float vw = viewportWidth; + const auto maxHorizontalInsetPx = static_cast(emSize * MAX_HORIZONTAL_INSET_EM); // Resolve all CssLength values to pixels using the current font's em size and viewport width blockStyle.marginTop = cssStyle.marginTop.toPixelsInt16(emSize, vw); blockStyle.marginBottom = cssStyle.marginBottom.toPixelsInt16(emSize, vw); - blockStyle.marginLeft = cssStyle.marginLeft.toPixelsInt16(emSize, vw); - blockStyle.marginRight = cssStyle.marginRight.toPixelsInt16(emSize, vw); + blockStyle.marginLeft = std::min(cssStyle.marginLeft.toPixelsInt16(emSize, vw), maxHorizontalInsetPx); + blockStyle.marginRight = std::min(cssStyle.marginRight.toPixelsInt16(emSize, vw), maxHorizontalInsetPx); blockStyle.paddingTop = cssStyle.paddingTop.toPixelsInt16(emSize, vw); blockStyle.paddingBottom = cssStyle.paddingBottom.toPixelsInt16(emSize, vw); - blockStyle.paddingLeft = cssStyle.paddingLeft.toPixelsInt16(emSize, vw); - blockStyle.paddingRight = cssStyle.paddingRight.toPixelsInt16(emSize, vw); + blockStyle.paddingLeft = std::min(cssStyle.paddingLeft.toPixelsInt16(emSize, vw), maxHorizontalInsetPx); + blockStyle.paddingRight = std::min(cssStyle.paddingRight.toPixelsInt16(emSize, vw), maxHorizontalInsetPx); // For textIndent: if it's a percentage we can't resolve (no viewport width), // leave textIndentDefined=false so the EmSpace fallback in applyParagraphIndent() is used