From 4537c55e84c688e3b24dba2ba1f53126be1657bc Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Sat, 10 Jan 2026 10:30:21 +0100 Subject: [PATCH] fix(css): respect css for page break and minimum font size, closes #2895 (#2903) --- apps/readest-app/src/utils/style.ts | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/apps/readest-app/src/utils/style.ts b/apps/readest-app/src/utils/style.ts index d8f563b4..18e6742c 100644 --- a/apps/readest-app/src/utils/style.ts +++ b/apps/readest-app/src/utils/style.ts @@ -411,6 +411,14 @@ const getLayoutStyles = ( height: auto; } + /* page break */ + body.paginated-mode div[style*="page-break-after: always"], + body.paginated-mode div[style*="page-break-after:always"], + body.paginated-mode p[style*="page-break-after: always"], + body.paginated-mode p[style*="page-break-after:always"] { + margin-bottom: 100vh !important; + } + /* workaround for some badly designed epubs */ div.left *, p.left * { text-align: left; } div.right *, p.right * { text-align: right; } @@ -610,6 +618,7 @@ export const applyTranslationStyle = (viewSettings: ViewSettings) => { export const transformStylesheet = (css: string, vw: number, vh: number, vertical: boolean) => { const isMobile = ['ios', 'android'].includes(getOSPlatform()); const fontScale = isMobile ? 1.25 : 1; + const isInlineStyle = !css.includes('{'); const ruleRegex = /([^{]+)({[^}]+})/g; css = css.replace(ruleRegex, (match, selector, block) => { const hasTextAlignCenter = /text-align\s*:\s*center\s*[;$]/.test(block); @@ -638,6 +647,24 @@ export const transformStylesheet = (css: string, vw: number, vh: number, vertica return match; }); + if (isInlineStyle) { + const hasPageBreakAfterAlways = /page-break-after\s*:\s*always\s*[;]?/.test(css); + if (hasPageBreakAfterAlways && !/margin-bottom\s*:/.test(css)) { + css = css.replace(/;?\s*$/, '') + '; margin-bottom: 100vh !important'; + } + } else { + css = css.replace(ruleRegex, (match, selector, block) => { + const hasPageBreakAfterAlways = /page-break-after\s*:\s*always\s*[;$]/.test(block); + if (hasPageBreakAfterAlways) { + if (!/margin-bottom\s*:/.test(block)) { + block = block.replace(/}$/, ' margin-bottom: 100vh !important; }'); + } + return selector + block; + } + return match; + }); + } + // Process duokan-bleed css = css.replace(ruleRegex, (_, selector, block) => { if (vertical) return selector + block; @@ -710,6 +737,9 @@ export const transformStylesheet = (css: string, vw: number, vh: number, vertica const rem = parseFloat(pt) / fontScale / 12; return `font-size: ${rem}rem`; }) + .replace(/font-size\s*:\s*(\d*\.?\d+)(px|rem|em|%)?/gi, (_, size, unit = 'px') => { + return `font-size: max(${size}${unit}, var(--min-font-size, 8px))`; + }) .replace(/(\d*\.?\d+)vw/gi, (_, d) => (parseFloat(d) * vw) / 100 + 'px') .replace(/(\d*\.?\d+)vh/gi, (_, d) => (parseFloat(d) * vh) / 100 + 'px') .replace(/([\s;])font-family\s*:\s*monospace/gi, '$1font-family: var(--monospace)')