From 2fd21e234249a8cf0ac723c42190fda81e7b0cac Mon Sep 17 00:00:00 2001 From: k6G52m4Dz75W <74605402+k6G52m4Dz75W@users.noreply.github.com> Date: Sat, 15 Aug 2026 20:01:41 +0800 Subject: [PATCH] fix(reader): exact page margins via foliate gap + paper sizing Desktop + mobile shared page-margin calculation: - foliate-js: the gap attribute was conflated with margin (case 'gap' set the four --_margin-* vars), so --_gap never updated from the attribute and stayed at its 7% default; the gapPercent computed by callers was dead. Restore upstream semantics: gap -> --_gap, margin -> the four --_margin-*. - desktop (FoliateViewer): derive max-inline-size + gap from renderer.clientWidth (foliate resolves the gap % against it). A double spread is two columns of ONE iframe, so the center gutter = column-gap = effective gap / 2; solving gutter = margin and outer = margin + breathing room (paper = 99% of window, so pages never touch the window edges even at margin 0) yields g = 2M/(0.99H + M), max-inline = (paper - M + g.H)/2 for double and g = M/paper, max-inline = paper - M + g.H for single. No rounding -> exact. - desktop: re-apply layout when pageMargin changes and on window resize. - mobile (reader.template.html): replace the margin/393 heuristic with the same single-page formula via applyMobileReflowMargins (margin-top/bottom, margin-left/right 0, gap + max-inline from renderer.clientWidth); rebuilt reader.html. --- packages/app-expo/assets/reader/reader.html | 85 ++++++++++--------- .../assets/reader/reader.template.html | 37 ++++---- .../src/components/reader/FoliateViewer.tsx | 63 +++++++++++--- packages/foliate-js/paginator.js | 7 ++ 4 files changed, 126 insertions(+), 66 deletions(-) diff --git a/packages/app-expo/assets/reader/reader.html b/packages/app-expo/assets/reader/reader.html index 2463249a..ec9006c9 100644 --- a/packages/app-expo/assets/reader/reader.html +++ b/packages/app-expo/assets/reader/reader.html @@ -292,17 +292,29 @@ } } - function getMobileReflowMaxInlineSize() { - const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 720; - const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 393; - return Math.ceil(Math.max(720, viewportHeight, viewportWidth)); - } - function applyMobileReflowRendererSizing(renderer) { if (!renderer) return; renderer.setAttribute('max-column-count', '1'); renderer.setAttribute('max-block-size', '1440px'); - renderer.setAttribute('max-inline-size', getMobileReflowMaxInlineSize() + 'px'); + } + + // Mobile is always single-page (no spread), so there is no column gutter to + // balance — the foliate gap only contributes the page's internal side + // padding. Same derivation as the desktop reader: paper = 99% of the window + // (breathing room even at margin 0), outer text inset = margin + 0.5%·H. + // All against renderer.clientWidth (foliate resolves the gap % against it). + function applyMobileReflowMargins(renderer, margin) { + if (!renderer) return; + const windowWidth = + renderer.clientWidth || window.innerWidth || document.documentElement.clientWidth || 393; + const paper = windowWidth * 0.99; + const g = margin / paper; + renderer.setAttribute('margin-top', margin + 'px'); + renderer.setAttribute('margin-bottom', margin + 'px'); + renderer.setAttribute('margin-left', '0px'); + renderer.setAttribute('margin-right', '0px'); + renderer.setAttribute('gap', g * 100 + '%'); + renderer.setAttribute('max-inline-size', paper - margin + g * windowWidth + 'px'); } function applyRendererFlowMode(renderer) { @@ -1715,11 +1727,8 @@ const renderer = el.renderer; if (renderer && !el.isFixedLayout) { applyMobileReflowRendererSizing(renderer); - // Default gap/margin - const margin = msg.pageMargin || 16; - const gapPercent = Math.max(1, Math.round((margin / 393) * 100)); - renderer.setAttribute('gap', gapPercent + '%'); - renderer.setAttribute('margin', margin + 'px'); + // Default margins (synchronized with the desktop reader formula) + applyMobileReflowMargins(renderer, msg.pageMargin || 16); } else if (renderer && el.isFixedLayout) { const paginatedLayout = msg.paginatedLayout === 'single' || msg.paginatedLayout === 'double' @@ -1973,9 +1982,7 @@ const BASELINE_FONT_SIZE = 16; const layoutScale = currentFontSize / BASELINE_FONT_SIZE; const margin = Math.round(settings.pageMargin * layoutScale); - const gapPercent = Math.max(1, Math.round((margin / 393) * 100)); - renderer.setAttribute('gap', gapPercent + '%'); - renderer.setAttribute('margin', margin + 'px'); + applyMobileReflowMargins(renderer, margin); } if (view && view.isFixedLayout) { @@ -4928,7 +4935,7 @@