fix(reader): hide footnote aside border again when custom fonts are loaded (#4438) (#4540)

PR #4383 inlined custom `@font-face` rules at the very front of the iframe
stylesheet, ahead of the `@namespace epub` declaration that lived inside
`getPageLayoutStyles`. Per the CSS spec a `@namespace` rule is only honored
when it precedes every style and `@font-face` rule; a misplaced one is
silently ignored. That dropped the namespaced
`aside[epub|type~="footnote"]` hide rule, so EPUBs whose footnote `<aside>`
carries a `border: 3px #333 double` rendered a stray horizontal line below
the annotation marker — but only for users who had custom fonts loaded
(otherwise `customFontFaces` is empty and `@namespace` stayed first).

Hoist the `@namespace` declaration to the very start of the assembled
stylesheet, before the inlined custom `@font-face` rules, and drop it from
`getPageLayoutStyles`. Custom faces still precede the `--serif`/`--sans-serif`
font lists that reference them, preserving #4383's first-paint behavior.

Verified in Chromium against the reported book's CSS: the aside goes from
`display: block` (3px double border visible) back to `display: none`.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-12 02:32:39 +08:00
committed by GitHub
parent 755bee1ee6
commit d6e981e568
2 changed files with 25 additions and 7 deletions
@@ -790,13 +790,26 @@ describe('custom @font-face inlining (via getStyles)', () => {
expect(css).toContain('blob:http://localhost/my-test-font');
});
it('inlines the @font-face rules ahead of the rest of the stylesheet', () => {
it('keeps @namespace epub ahead of every @font-face rule (#4438)', () => {
const vs = makeViewSettings();
const css = getStyles(vs, theme, [makeCustomFont()]);
// Paginator writes this CSS into the iframe before its first paint,
// so the custom @font-face must precede the font-family declarations
// that reference it (layout styles begin with `@namespace epub`).
expect(css.indexOf('@font-face')).toBeLessThan(css.indexOf('@namespace epub'));
// A `@namespace` rule is only honored when it precedes all style and
// `@font-face` rules; a misplaced one is silently ignored, which drops
// the namespaced `aside[epub|type~="footnote"]` selector and reveals the
// footnote aside's border as a stray horizontal line (#4438). The custom
// `@font-face` rules must therefore come after the namespace declaration.
expect(css).toContain('@namespace epub');
expect(css).toContain('@font-face');
expect(css.indexOf('@namespace epub')).toBeLessThan(css.indexOf('@font-face'));
});
it('still inlines custom @font-face ahead of the font-family declarations', () => {
const vs = makeViewSettings();
const css = getStyles(vs, theme, [makeCustomFont()]);
// Paginator writes this CSS into the iframe before its first paint, so the
// custom `@font-face` rules should still precede the `--serif`/`--sans-serif`
// font lists that reference them.
expect(css.indexOf('@font-face')).toBeLessThan(css.indexOf('--serif:'));
});
it('emits one @font-face per loaded font', () => {
+7 -2
View File
@@ -347,7 +347,6 @@ const getPageLayoutStyles = (
writingMode: string,
vertical: boolean,
) => `
@namespace epub "http://www.idpf.org/2007/ops";
html {
--margin-top: ${marginTop}px;
--margin-right: ${marginRight}px;
@@ -867,7 +866,13 @@ export const getStyles = (
const warichuStyles = getWarichuStyles();
const rubyStyles = getRubyStyles();
const userStylesheet = viewSettings.userStylesheet!;
return `${customFontFaces}\n${pageLayoutStyles}\n${paragraphLayoutStyles}\n${fontStyles}\n${colorStyles}\n${translationStyles}\n${warichuStyles}\n${rubyStyles}\n${userStylesheet}`;
// The `@namespace` declaration must lead the stylesheet: a `@namespace` rule
// placed after any style or `@font-face` rule is invalid and silently ignored,
// which drops the namespaced `aside[epub|type~="footnote"]` selector and lets
// the footnote aside's border show as a stray horizontal line (#4438). Keep it
// ahead of the inlined custom `@font-face` rules.
const epubNamespace = `@namespace epub "http://www.idpf.org/2007/ops";`;
return `${epubNamespace}\n${customFontFaces}\n${pageLayoutStyles}\n${paragraphLayoutStyles}\n${fontStyles}\n${colorStyles}\n${translationStyles}\n${warichuStyles}\n${rubyStyles}\n${userStylesheet}`;
};
// Build a CSS chunk of `@font-face` rules for the given user custom