From 099591b7f0489470b27a9fe93c12e22cc21060ad Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Fri, 16 Jan 2026 13:45:07 -0500 Subject: [PATCH 1/3] claude: Add ensureCssRegexMatches test assertion Add a new test assertion function that checks regex patterns against CSS files in a document's supporting files directory. This is useful for verifying CSS content that is generated during rendering. Co-Authored-By: Claude Opus 4.5 --- tests/smoke/smoke-all.test.ts | 2 ++ tests/verify.ts | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/tests/smoke/smoke-all.test.ts b/tests/smoke/smoke-all.test.ts index b5aaafa6897..b92c9479aff 100644 --- a/tests/smoke/smoke-all.test.ts +++ b/tests/smoke/smoke-all.test.ts @@ -18,6 +18,7 @@ import { breakQuartoMd } from "../../src/core/lib/break-quarto-md.ts"; import { parse } from "../../src/core/yaml.ts"; import { cleanoutput } from "./render/render.ts"; import { + ensureCssRegexMatches, ensureEpubFileRegexMatches, ensureDocxRegexMatches, ensureDocxXpath, @@ -171,6 +172,7 @@ function resolveTestSpecs( const result = []; // deno-lint-ignore no-explicit-any const verifyMap: Record = { + ensureCssRegexMatches, ensureEpubFileRegexMatches, ensureHtmlElements, ensureHtmlElementContents, diff --git a/tests/verify.ts b/tests/verify.ts index a38dbbd01d5..524ad8f5eed 100644 --- a/tests/verify.ts +++ b/tests/verify.ts @@ -584,6 +584,53 @@ export const ensureFileRegexMatches = ( return(verifyFileRegexMatches(regexChecker)(file, matchesUntyped, noMatchesUntyped)); }; +// Use this function to Regex match text in CSS files in the supporting files directory +export const ensureCssRegexMatches = ( + file: string, + matchesUntyped: (string | RegExp)[], + noMatchesUntyped?: (string | RegExp)[], +): Verify => { + const asRegexp = (m: string | RegExp) => { + if (typeof m === "string") { + return new RegExp(m, "m"); + } + return m; + }; + const matches = matchesUntyped.map(asRegexp); + const noMatches = noMatchesUntyped?.map(asRegexp); + + return { + name: `Inspecting CSS files for Regex matches`, + verify: async (_output: ExecuteOutput[]) => { + // Find support directory from file path + const [dir, stem] = dirAndStem(file); + const supportDir = join(dir, stem + "_files"); + + // Find all CSS files recursively and combine their content + let combinedContent = ""; + for (const entry of walkSync(supportDir, { exts: [".css"] })) { + combinedContent += await Deno.readTextFile(entry.path) + "\n"; + } + + matches.forEach((regex) => { + assert( + regex.test(combinedContent), + `Required CSS match ${String(regex)} is missing.`, + ); + }); + + if (noMatches) { + noMatches.forEach((regex) => { + assert( + !regex.test(combinedContent), + `Illegal CSS match ${String(regex)} was found.`, + ); + }); + } + }, + }; +}; + // Use this function to Regex match text in the intermediate kept file // FIXME: do this properly without resorting on file having keep-* export const verifyKeepFileRegexMatches = ( From eed83c3ffe34b6c00377d03beacb8c75c76d0699 Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Fri, 16 Jan 2026 13:45:22 -0500 Subject: [PATCH 2/3] claude: Fix remote fonts not recognised in brand extensions (#13685) When a brand.yml file with remote font URLs (e.g., https://...) is located in a subfolder (like _extensions/my-brand/), the font URL was incorrectly joined with the path prefix, resulting in broken paths like "_extensions/my-brand/https:/...". Add isExternalPath helper to detect URLs and skip path joining for external font paths. Co-Authored-By: Claude Opus 4.5 --- src/core/sass/brand.ts | 7 ++++++- .../typography/remote-font-extension/.gitignore | 2 ++ .../_extensions/my-brand/_extension.yml | 8 ++++++++ .../_extensions/my-brand/mybrand.yml | 8 ++++++++ .../typography/remote-font-extension/_quarto.yml | 5 +++++ .../remote-font-extension.qmd | 15 +++++++++++++++ 6 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/docs/smoke-all/brand/typography/remote-font-extension/.gitignore create mode 100644 tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/_extension.yml create mode 100644 tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/mybrand.yml create mode 100644 tests/docs/smoke-all/brand/typography/remote-font-extension/_quarto.yml create mode 100644 tests/docs/smoke-all/brand/typography/remote-font-extension/remote-font-extension.qmd diff --git a/src/core/sass/brand.ts b/src/core/sass/brand.ts index 993fb5eac0a..51036ccbddc 100644 --- a/src/core/sass/brand.ts +++ b/src/core/sass/brand.ts @@ -150,6 +150,8 @@ const googleFontImportString = (description: BrandFontGoogle) => { }:${styleString}wght@${weights}&display=${display}');`; }; +const isExternalPath = (path: string) => /^\w+:/.test(path); + const fileFontImportString = (brand: Brand, description: BrandFontFile) => { const pathPrefix = relative(brand.projectDir, brand.brandDir); const parts = []; @@ -162,9 +164,12 @@ const fileFontImportString = (brand: Brand, description: BrandFontFile) => { weight = file.weight; style = file.style; } + const fontUrl = isExternalPath(path) + ? path + : join(pathPrefix, path).replace(/\\/g, "/"); parts.push(`@font-face { font-family: '${description.family}'; - src: url('${join(pathPrefix, path).replace(/\\/g, "/")}'); + src: url('${fontUrl}'); font-weight: ${weight || "normal"}; font-style: ${style || "normal"}; }\n`); diff --git a/tests/docs/smoke-all/brand/typography/remote-font-extension/.gitignore b/tests/docs/smoke-all/brand/typography/remote-font-extension/.gitignore new file mode 100644 index 00000000000..ad293093b07 --- /dev/null +++ b/tests/docs/smoke-all/brand/typography/remote-font-extension/.gitignore @@ -0,0 +1,2 @@ +/.quarto/ +**/*.quarto_ipynb diff --git a/tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/_extension.yml b/tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/_extension.yml new file mode 100644 index 00000000000..859925eab79 --- /dev/null +++ b/tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/_extension.yml @@ -0,0 +1,8 @@ +title: My Brand +author: Quarto +version: 1.0.0 +quarto-required: ">=99.9.0" +contributes: + metadata: + project: + brand: mybrand.yml diff --git a/tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/mybrand.yml b/tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/mybrand.yml new file mode 100644 index 00000000000..1f8fcf43c9d --- /dev/null +++ b/tests/docs/smoke-all/brand/typography/remote-font-extension/_extensions/my-brand/mybrand.yml @@ -0,0 +1,8 @@ +typography: + fonts: + - family: Noto Sans + source: file + files: + - path: https://notofonts.github.io/latin-greek-cyrillic/fonts/NotoSans/unhinted/ttf/NotoSans-Regular.ttf + base: + family: Noto Sans diff --git a/tests/docs/smoke-all/brand/typography/remote-font-extension/_quarto.yml b/tests/docs/smoke-all/brand/typography/remote-font-extension/_quarto.yml new file mode 100644 index 00000000000..f98ff850162 --- /dev/null +++ b/tests/docs/smoke-all/brand/typography/remote-font-extension/_quarto.yml @@ -0,0 +1,5 @@ +project: + type: default +format: + html: + theme: brand diff --git a/tests/docs/smoke-all/brand/typography/remote-font-extension/remote-font-extension.qmd b/tests/docs/smoke-all/brand/typography/remote-font-extension/remote-font-extension.qmd new file mode 100644 index 00000000000..25812585524 --- /dev/null +++ b/tests/docs/smoke-all/brand/typography/remote-font-extension/remote-font-extension.qmd @@ -0,0 +1,15 @@ +--- +title: Remote Font Extension Test +_quarto: + tests: + html: + ensureCssRegexMatches: + - ['src:url\("https://notofonts\.github\.io/'] + - ['_extensions/my-brand/https:'] +--- + +# Remote Font Test + +This document tests that remote font URLs in brand extensions are handled correctly (issue #13685). + +{{< lipsum 1 >}} From fd3eb04a50ac076e554b9000cf1b947ebfea0b0e Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Fri, 16 Jan 2026 14:25:17 -0500 Subject: [PATCH 3/3] claude: Add changelog entry for #13685 Co-Authored-By: Claude Opus 4.5 --- news/changelog-1.9.md | 1 + 1 file changed, 1 insertion(+) diff --git a/news/changelog-1.9.md b/news/changelog-1.9.md index 14f341aa6d9..1f55af4301e 100644 --- a/news/changelog-1.9.md +++ b/news/changelog-1.9.md @@ -32,6 +32,7 @@ All changes included in 1.9: - ([#11929](https://github.com/quarto-dev/quarto-cli/issues/11929)): Import all `brand.typography.fonts` in CSS, whether or not fonts are referenced by typography elements. - ([#13413](https://github.com/quarto-dev/quarto-cli/issues/13413)): Fix uncentered play button in `video` shortcodes from cross-reference divs. (author: @bruvellu) - ([#13508](https://github.com/quarto-dev/quarto-cli/issues/13508)): Add `aria-label` support to `video` shortcode for improved accessibility. +- ([#13685](https://github.com/quarto-dev/quarto-cli/issues/13685)): Fix remote font URLs in brand extensions being incorrectly joined with the extension path, resulting in broken font imports. ### `typst`